##// END OF EJS Templates
ssh: show fingerprint when adding already existing key....
marcink -
r2594:dc2a5f04 default
parent child Browse files
Show More
@@ -1,173 +1,176 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.model.db import User, UserSshKeys
23 from rhodecode.model.db import User, UserSshKeys
24
24
25 from rhodecode.tests import TestController, assert_session_flash
25 from rhodecode.tests import TestController, assert_session_flash
26 from rhodecode.tests.fixture import Fixture
26 from rhodecode.tests.fixture import Fixture
27
27
28 fixture = Fixture()
28 fixture = Fixture()
29
29
30
30
31 def route_path(name, params=None, **kwargs):
31 def route_path(name, params=None, **kwargs):
32 import urllib
32 import urllib
33 from rhodecode.apps._base import ADMIN_PREFIX
33 from rhodecode.apps._base import ADMIN_PREFIX
34
34
35 base_url = {
35 base_url = {
36 'edit_user_ssh_keys':
36 'edit_user_ssh_keys':
37 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys',
37 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys',
38 'edit_user_ssh_keys_generate_keypair':
38 'edit_user_ssh_keys_generate_keypair':
39 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/generate',
39 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/generate',
40 'edit_user_ssh_keys_add':
40 'edit_user_ssh_keys_add':
41 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/new',
41 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/new',
42 'edit_user_ssh_keys_delete':
42 'edit_user_ssh_keys_delete':
43 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/delete',
43 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/delete',
44
44
45 }[name].format(**kwargs)
45 }[name].format(**kwargs)
46
46
47 if params:
47 if params:
48 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
48 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
49 return base_url
49 return base_url
50
50
51
51
52 class TestAdminUsersSshKeysView(TestController):
52 class TestAdminUsersSshKeysView(TestController):
53 INVALID_KEY = """\
53 INVALID_KEY = """\
54 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vevJsuZds1iNU5
54 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vevJsuZds1iNU5
55 LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSykfR1D1TdluyIpQLrwgH5kb
55 LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSykfR1D1TdluyIpQLrwgH5kb
56 n8FkVI8zBMCKakxowvN67B0R7b1BT4PPzW2JlOXei/m9W12ZY484VTow6/B+kf2Q8
56 n8FkVI8zBMCKakxowvN67B0R7b1BT4PPzW2JlOXei/m9W12ZY484VTow6/B+kf2Q8
57 cP8tmCJmKWZma5Em7OTUhvjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6
57 cP8tmCJmKWZma5Em7OTUhvjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6
58 jvdphZTc30I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zP
58 jvdphZTc30I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zP
59 qPFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL
59 qPFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL
60 your_email@example.com
60 your_email@example.com
61 """
61 """
62 VALID_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vev' \
62 VALID_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vev' \
63 'JsuZds1iNU5LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSy' \
63 'JsuZds1iNU5LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSy' \
64 'kfR1D1TdluyIpQLrwgH5kbn8FkVI8zBMCKakxowvN67B0R7b1BT4PP' \
64 'kfR1D1TdluyIpQLrwgH5kbn8FkVI8zBMCKakxowvN67B0R7b1BT4PP' \
65 'zW2JlOXei/m9W12ZY484VTow6/B+kf2Q8cP8tmCJmKWZma5Em7OTUh' \
65 'zW2JlOXei/m9W12ZY484VTow6/B+kf2Q8cP8tmCJmKWZma5Em7OTUh' \
66 'vjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6jvdphZTc30' \
66 'vjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6jvdphZTc30' \
67 'I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zPq' \
67 'I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zPq' \
68 'PFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL ' \
68 'PFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL ' \
69 'your_email@example.com'
69 'your_email@example.com'
70 FINGERPRINT = 'MD5:01:4f:ad:29:22:6e:01:37:c9:d2:52:26:52:b0:2d:93'
70
71
71 def test_ssh_keys_default_user(self):
72 def test_ssh_keys_default_user(self):
72 self.log_user()
73 self.log_user()
73 user = User.get_default_user()
74 user = User.get_default_user()
74 self.app.get(
75 self.app.get(
75 route_path('edit_user_ssh_keys', user_id=user.user_id),
76 route_path('edit_user_ssh_keys', user_id=user.user_id),
76 status=302)
77 status=302)
77
78
78 def test_add_ssh_key_error(self, user_util):
79 def test_add_ssh_key_error(self, user_util):
79 self.log_user()
80 self.log_user()
80 user = user_util.create_user()
81 user = user_util.create_user()
81 user_id = user.user_id
82 user_id = user.user_id
82
83
83 key_data = self.INVALID_KEY
84 key_data = self.INVALID_KEY
84
85
85 desc = 'MY SSH KEY'
86 desc = 'MY SSH KEY'
86 response = self.app.post(
87 response = self.app.post(
87 route_path('edit_user_ssh_keys_add', user_id=user_id),
88 route_path('edit_user_ssh_keys_add', user_id=user_id),
88 {'description': desc, 'key_data': key_data,
89 {'description': desc, 'key_data': key_data,
89 'csrf_token': self.csrf_token})
90 'csrf_token': self.csrf_token})
90 assert_session_flash(response, 'An error occurred during ssh '
91 assert_session_flash(response, 'An error occurred during ssh '
91 'key saving: Unable to decode the key')
92 'key saving: Unable to decode the key')
92
93
93 def test_ssh_key_duplicate(self, user_util):
94 def test_ssh_key_duplicate(self, user_util):
94 self.log_user()
95 self.log_user()
95 user = user_util.create_user()
96 user = user_util.create_user()
96 user_id = user.user_id
97 user_id = user.user_id
97
98
98 key_data = self.VALID_KEY
99 key_data = self.VALID_KEY
99
100
100 desc = 'MY SSH KEY'
101 desc = 'MY SSH KEY'
101 response = self.app.post(
102 response = self.app.post(
102 route_path('edit_user_ssh_keys_add', user_id=user_id),
103 route_path('edit_user_ssh_keys_add', user_id=user_id),
103 {'description': desc, 'key_data': key_data,
104 {'description': desc, 'key_data': key_data,
104 'csrf_token': self.csrf_token})
105 'csrf_token': self.csrf_token})
105 assert_session_flash(response, 'Ssh Key successfully created')
106 assert_session_flash(response, 'Ssh Key successfully created')
106 response.follow() # flush session flash
107 response.follow() # flush session flash
107
108
108 # add the same key AGAIN
109 # add the same key AGAIN
109 desc = 'MY SSH KEY'
110 desc = 'MY SSH KEY'
110 response = self.app.post(
111 response = self.app.post(
111 route_path('edit_user_ssh_keys_add', user_id=user_id),
112 route_path('edit_user_ssh_keys_add', user_id=user_id),
112 {'description': desc, 'key_data': key_data,
113 {'description': desc, 'key_data': key_data,
113 'csrf_token': self.csrf_token})
114 'csrf_token': self.csrf_token})
115
116 err = 'Such key with fingerprint `{}` already exists, ' \
117 'please use a different one'.format(self.FINGERPRINT)
114 assert_session_flash(response, 'An error occurred during ssh key '
118 assert_session_flash(response, 'An error occurred during ssh key '
115 'saving: Such key already exists, '
119 'saving: {}'.format(err))
116 'please use a different one')
117
120
118 def test_add_ssh_key(self, user_util):
121 def test_add_ssh_key(self, user_util):
119 self.log_user()
122 self.log_user()
120 user = user_util.create_user()
123 user = user_util.create_user()
121 user_id = user.user_id
124 user_id = user.user_id
122
125
123 key_data = self.VALID_KEY
126 key_data = self.VALID_KEY
124
127
125 desc = 'MY SSH KEY'
128 desc = 'MY SSH KEY'
126 response = self.app.post(
129 response = self.app.post(
127 route_path('edit_user_ssh_keys_add', user_id=user_id),
130 route_path('edit_user_ssh_keys_add', user_id=user_id),
128 {'description': desc, 'key_data': key_data,
131 {'description': desc, 'key_data': key_data,
129 'csrf_token': self.csrf_token})
132 'csrf_token': self.csrf_token})
130 assert_session_flash(response, 'Ssh Key successfully created')
133 assert_session_flash(response, 'Ssh Key successfully created')
131
134
132 response = response.follow()
135 response = response.follow()
133 response.mustcontain(desc)
136 response.mustcontain(desc)
134
137
135 def test_delete_ssh_key(self, user_util):
138 def test_delete_ssh_key(self, user_util):
136 self.log_user()
139 self.log_user()
137 user = user_util.create_user()
140 user = user_util.create_user()
138 user_id = user.user_id
141 user_id = user.user_id
139
142
140 key_data = self.VALID_KEY
143 key_data = self.VALID_KEY
141
144
142 desc = 'MY SSH KEY'
145 desc = 'MY SSH KEY'
143 response = self.app.post(
146 response = self.app.post(
144 route_path('edit_user_ssh_keys_add', user_id=user_id),
147 route_path('edit_user_ssh_keys_add', user_id=user_id),
145 {'description': desc, 'key_data': key_data,
148 {'description': desc, 'key_data': key_data,
146 'csrf_token': self.csrf_token})
149 'csrf_token': self.csrf_token})
147 assert_session_flash(response, 'Ssh Key successfully created')
150 assert_session_flash(response, 'Ssh Key successfully created')
148 response = response.follow() # flush the Session flash
151 response = response.follow() # flush the Session flash
149
152
150 # now delete our key
153 # now delete our key
151 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
154 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
152 assert 1 == len(keys)
155 assert 1 == len(keys)
153
156
154 response = self.app.post(
157 response = self.app.post(
155 route_path('edit_user_ssh_keys_delete', user_id=user_id),
158 route_path('edit_user_ssh_keys_delete', user_id=user_id),
156 {'del_ssh_key': keys[0].ssh_key_id,
159 {'del_ssh_key': keys[0].ssh_key_id,
157 'csrf_token': self.csrf_token})
160 'csrf_token': self.csrf_token})
158
161
159 assert_session_flash(response, 'Ssh key successfully deleted')
162 assert_session_flash(response, 'Ssh key successfully deleted')
160 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
163 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
161 assert 0 == len(keys)
164 assert 0 == len(keys)
162
165
163 def test_generate_keypair(self, user_util):
166 def test_generate_keypair(self, user_util):
164 self.log_user()
167 self.log_user()
165 user = user_util.create_user()
168 user = user_util.create_user()
166 user_id = user.user_id
169 user_id = user.user_id
167
170
168 response = self.app.get(
171 response = self.app.get(
169 route_path('edit_user_ssh_keys_generate_keypair', user_id=user_id))
172 route_path('edit_user_ssh_keys_generate_keypair', user_id=user_id))
170
173
171 response.mustcontain('Private key')
174 response.mustcontain('Private key')
172 response.mustcontain('Public key')
175 response.mustcontain('Public key')
173 response.mustcontain('-----BEGIN RSA PRIVATE KEY-----')
176 response.mustcontain('-----BEGIN RSA PRIVATE KEY-----')
@@ -1,1189 +1,1191 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.authentication.plugins import auth_rhodecode
33 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.events import trigger
34 from rhodecode.events import trigger
35
35
36 from rhodecode.lib import audit_logger
36 from rhodecode.lib import audit_logger
37 from rhodecode.lib.exceptions import (
37 from rhodecode.lib.exceptions import (
38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
39 UserOwnsUserGroupsException, DefaultUserException)
39 UserOwnsUserGroupsException, DefaultUserException)
40 from rhodecode.lib.ext_json import json
40 from rhodecode.lib.ext_json import json
41 from rhodecode.lib.auth import (
41 from rhodecode.lib.auth import (
42 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
42 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
44 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
45 from rhodecode.model.auth_token import AuthTokenModel
45 from rhodecode.model.auth_token import AuthTokenModel
46 from rhodecode.model.forms import (
46 from rhodecode.model.forms import (
47 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
47 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
48 UserExtraEmailForm, UserExtraIpForm)
48 UserExtraEmailForm, UserExtraIpForm)
49 from rhodecode.model.permission import PermissionModel
49 from rhodecode.model.permission import PermissionModel
50 from rhodecode.model.repo_group import RepoGroupModel
50 from rhodecode.model.repo_group import RepoGroupModel
51 from rhodecode.model.ssh_key import SshKeyModel
51 from rhodecode.model.ssh_key import SshKeyModel
52 from rhodecode.model.user import UserModel
52 from rhodecode.model.user import UserModel
53 from rhodecode.model.user_group import UserGroupModel
53 from rhodecode.model.user_group import UserGroupModel
54 from rhodecode.model.db import (
54 from rhodecode.model.db import (
55 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
55 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
56 UserApiKeys, UserSshKeys, RepoGroup)
56 UserApiKeys, UserSshKeys, RepoGroup)
57 from rhodecode.model.meta import Session
57 from rhodecode.model.meta import Session
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class AdminUsersView(BaseAppView, DataGridAppView):
62 class AdminUsersView(BaseAppView, DataGridAppView):
63
63
64 def load_default_context(self):
64 def load_default_context(self):
65 c = self._get_local_tmpl_context()
65 c = self._get_local_tmpl_context()
66 return c
66 return c
67
67
68 @LoginRequired()
68 @LoginRequired()
69 @HasPermissionAllDecorator('hg.admin')
69 @HasPermissionAllDecorator('hg.admin')
70 @view_config(
70 @view_config(
71 route_name='users', request_method='GET',
71 route_name='users', request_method='GET',
72 renderer='rhodecode:templates/admin/users/users.mako')
72 renderer='rhodecode:templates/admin/users/users.mako')
73 def users_list(self):
73 def users_list(self):
74 c = self.load_default_context()
74 c = self.load_default_context()
75 return self._get_template_context(c)
75 return self._get_template_context(c)
76
76
77 @LoginRequired()
77 @LoginRequired()
78 @HasPermissionAllDecorator('hg.admin')
78 @HasPermissionAllDecorator('hg.admin')
79 @view_config(
79 @view_config(
80 # renderer defined below
80 # renderer defined below
81 route_name='users_data', request_method='GET',
81 route_name='users_data', request_method='GET',
82 renderer='json_ext', xhr=True)
82 renderer='json_ext', xhr=True)
83 def users_list_data(self):
83 def users_list_data(self):
84 self.load_default_context()
84 self.load_default_context()
85 column_map = {
85 column_map = {
86 'first_name': 'name',
86 'first_name': 'name',
87 'last_name': 'lastname',
87 'last_name': 'lastname',
88 }
88 }
89 draw, start, limit = self._extract_chunk(self.request)
89 draw, start, limit = self._extract_chunk(self.request)
90 search_q, order_by, order_dir = self._extract_ordering(
90 search_q, order_by, order_dir = self._extract_ordering(
91 self.request, column_map=column_map)
91 self.request, column_map=column_map)
92
92
93 _render = self.request.get_partial_renderer(
93 _render = self.request.get_partial_renderer(
94 'rhodecode:templates/data_table/_dt_elements.mako')
94 'rhodecode:templates/data_table/_dt_elements.mako')
95
95
96 def user_actions(user_id, username):
96 def user_actions(user_id, username):
97 return _render("user_actions", user_id, username)
97 return _render("user_actions", user_id, username)
98
98
99 users_data_total_count = User.query()\
99 users_data_total_count = User.query()\
100 .filter(User.username != User.DEFAULT_USER) \
100 .filter(User.username != User.DEFAULT_USER) \
101 .count()
101 .count()
102
102
103 # json generate
103 # json generate
104 base_q = User.query().filter(User.username != User.DEFAULT_USER)
104 base_q = User.query().filter(User.username != User.DEFAULT_USER)
105
105
106 if search_q:
106 if search_q:
107 like_expression = u'%{}%'.format(safe_unicode(search_q))
107 like_expression = u'%{}%'.format(safe_unicode(search_q))
108 base_q = base_q.filter(or_(
108 base_q = base_q.filter(or_(
109 User.username.ilike(like_expression),
109 User.username.ilike(like_expression),
110 User._email.ilike(like_expression),
110 User._email.ilike(like_expression),
111 User.name.ilike(like_expression),
111 User.name.ilike(like_expression),
112 User.lastname.ilike(like_expression),
112 User.lastname.ilike(like_expression),
113 ))
113 ))
114
114
115 users_data_total_filtered_count = base_q.count()
115 users_data_total_filtered_count = base_q.count()
116
116
117 sort_col = getattr(User, order_by, None)
117 sort_col = getattr(User, order_by, None)
118 if sort_col:
118 if sort_col:
119 if order_dir == 'asc':
119 if order_dir == 'asc':
120 # handle null values properly to order by NULL last
120 # handle null values properly to order by NULL last
121 if order_by in ['last_activity']:
121 if order_by in ['last_activity']:
122 sort_col = coalesce(sort_col, datetime.date.max)
122 sort_col = coalesce(sort_col, datetime.date.max)
123 sort_col = sort_col.asc()
123 sort_col = sort_col.asc()
124 else:
124 else:
125 # handle null values properly to order by NULL last
125 # handle null values properly to order by NULL last
126 if order_by in ['last_activity']:
126 if order_by in ['last_activity']:
127 sort_col = coalesce(sort_col, datetime.date.min)
127 sort_col = coalesce(sort_col, datetime.date.min)
128 sort_col = sort_col.desc()
128 sort_col = sort_col.desc()
129
129
130 base_q = base_q.order_by(sort_col)
130 base_q = base_q.order_by(sort_col)
131 base_q = base_q.offset(start).limit(limit)
131 base_q = base_q.offset(start).limit(limit)
132
132
133 users_list = base_q.all()
133 users_list = base_q.all()
134
134
135 users_data = []
135 users_data = []
136 for user in users_list:
136 for user in users_list:
137 users_data.append({
137 users_data.append({
138 "username": h.gravatar_with_user(self.request, user.username),
138 "username": h.gravatar_with_user(self.request, user.username),
139 "email": user.email,
139 "email": user.email,
140 "first_name": user.first_name,
140 "first_name": user.first_name,
141 "last_name": user.last_name,
141 "last_name": user.last_name,
142 "last_login": h.format_date(user.last_login),
142 "last_login": h.format_date(user.last_login),
143 "last_activity": h.format_date(user.last_activity),
143 "last_activity": h.format_date(user.last_activity),
144 "active": h.bool2icon(user.active),
144 "active": h.bool2icon(user.active),
145 "active_raw": user.active,
145 "active_raw": user.active,
146 "admin": h.bool2icon(user.admin),
146 "admin": h.bool2icon(user.admin),
147 "extern_type": user.extern_type,
147 "extern_type": user.extern_type,
148 "extern_name": user.extern_name,
148 "extern_name": user.extern_name,
149 "action": user_actions(user.user_id, user.username),
149 "action": user_actions(user.user_id, user.username),
150 })
150 })
151
151
152 data = ({
152 data = ({
153 'draw': draw,
153 'draw': draw,
154 'data': users_data,
154 'data': users_data,
155 'recordsTotal': users_data_total_count,
155 'recordsTotal': users_data_total_count,
156 'recordsFiltered': users_data_total_filtered_count,
156 'recordsFiltered': users_data_total_filtered_count,
157 })
157 })
158
158
159 return data
159 return data
160
160
161 def _set_personal_repo_group_template_vars(self, c_obj):
161 def _set_personal_repo_group_template_vars(self, c_obj):
162 DummyUser = AttributeDict({
162 DummyUser = AttributeDict({
163 'username': '${username}',
163 'username': '${username}',
164 'user_id': '${user_id}',
164 'user_id': '${user_id}',
165 })
165 })
166 c_obj.default_create_repo_group = RepoGroupModel() \
166 c_obj.default_create_repo_group = RepoGroupModel() \
167 .get_default_create_personal_repo_group()
167 .get_default_create_personal_repo_group()
168 c_obj.personal_repo_group_name = RepoGroupModel() \
168 c_obj.personal_repo_group_name = RepoGroupModel() \
169 .get_personal_group_name(DummyUser)
169 .get_personal_group_name(DummyUser)
170
170
171 @LoginRequired()
171 @LoginRequired()
172 @HasPermissionAllDecorator('hg.admin')
172 @HasPermissionAllDecorator('hg.admin')
173 @view_config(
173 @view_config(
174 route_name='users_new', request_method='GET',
174 route_name='users_new', request_method='GET',
175 renderer='rhodecode:templates/admin/users/user_add.mako')
175 renderer='rhodecode:templates/admin/users/user_add.mako')
176 def users_new(self):
176 def users_new(self):
177 _ = self.request.translate
177 _ = self.request.translate
178 c = self.load_default_context()
178 c = self.load_default_context()
179 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
179 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
180 self._set_personal_repo_group_template_vars(c)
180 self._set_personal_repo_group_template_vars(c)
181 return self._get_template_context(c)
181 return self._get_template_context(c)
182
182
183 @LoginRequired()
183 @LoginRequired()
184 @HasPermissionAllDecorator('hg.admin')
184 @HasPermissionAllDecorator('hg.admin')
185 @CSRFRequired()
185 @CSRFRequired()
186 @view_config(
186 @view_config(
187 route_name='users_create', request_method='POST',
187 route_name='users_create', request_method='POST',
188 renderer='rhodecode:templates/admin/users/user_add.mako')
188 renderer='rhodecode:templates/admin/users/user_add.mako')
189 def users_create(self):
189 def users_create(self):
190 _ = self.request.translate
190 _ = self.request.translate
191 c = self.load_default_context()
191 c = self.load_default_context()
192 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
192 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
193 user_model = UserModel()
193 user_model = UserModel()
194 user_form = UserForm(self.request.translate)()
194 user_form = UserForm(self.request.translate)()
195 try:
195 try:
196 form_result = user_form.to_python(dict(self.request.POST))
196 form_result = user_form.to_python(dict(self.request.POST))
197 user = user_model.create(form_result)
197 user = user_model.create(form_result)
198 Session().flush()
198 Session().flush()
199 creation_data = user.get_api_data()
199 creation_data = user.get_api_data()
200 username = form_result['username']
200 username = form_result['username']
201
201
202 audit_logger.store_web(
202 audit_logger.store_web(
203 'user.create', action_data={'data': creation_data},
203 'user.create', action_data={'data': creation_data},
204 user=c.rhodecode_user)
204 user=c.rhodecode_user)
205
205
206 user_link = h.link_to(
206 user_link = h.link_to(
207 h.escape(username),
207 h.escape(username),
208 h.route_path('user_edit', user_id=user.user_id))
208 h.route_path('user_edit', user_id=user.user_id))
209 h.flash(h.literal(_('Created user %(user_link)s')
209 h.flash(h.literal(_('Created user %(user_link)s')
210 % {'user_link': user_link}), category='success')
210 % {'user_link': user_link}), category='success')
211 Session().commit()
211 Session().commit()
212 except formencode.Invalid as errors:
212 except formencode.Invalid as errors:
213 self._set_personal_repo_group_template_vars(c)
213 self._set_personal_repo_group_template_vars(c)
214 data = render(
214 data = render(
215 'rhodecode:templates/admin/users/user_add.mako',
215 'rhodecode:templates/admin/users/user_add.mako',
216 self._get_template_context(c), self.request)
216 self._get_template_context(c), self.request)
217 html = formencode.htmlfill.render(
217 html = formencode.htmlfill.render(
218 data,
218 data,
219 defaults=errors.value,
219 defaults=errors.value,
220 errors=errors.error_dict or {},
220 errors=errors.error_dict or {},
221 prefix_error=False,
221 prefix_error=False,
222 encoding="UTF-8",
222 encoding="UTF-8",
223 force_defaults=False
223 force_defaults=False
224 )
224 )
225 return Response(html)
225 return Response(html)
226 except UserCreationError as e:
226 except UserCreationError as e:
227 h.flash(e, 'error')
227 h.flash(e, 'error')
228 except Exception:
228 except Exception:
229 log.exception("Exception creation of user")
229 log.exception("Exception creation of user")
230 h.flash(_('Error occurred during creation of user %s')
230 h.flash(_('Error occurred during creation of user %s')
231 % self.request.POST.get('username'), category='error')
231 % self.request.POST.get('username'), category='error')
232 raise HTTPFound(h.route_path('users'))
232 raise HTTPFound(h.route_path('users'))
233
233
234
234
235 class UsersView(UserAppView):
235 class UsersView(UserAppView):
236 ALLOW_SCOPED_TOKENS = False
236 ALLOW_SCOPED_TOKENS = False
237 """
237 """
238 This view has alternative version inside EE, if modified please take a look
238 This view has alternative version inside EE, if modified please take a look
239 in there as well.
239 in there as well.
240 """
240 """
241
241
242 def load_default_context(self):
242 def load_default_context(self):
243 c = self._get_local_tmpl_context()
243 c = self._get_local_tmpl_context()
244 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
244 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
245 c.allowed_languages = [
245 c.allowed_languages = [
246 ('en', 'English (en)'),
246 ('en', 'English (en)'),
247 ('de', 'German (de)'),
247 ('de', 'German (de)'),
248 ('fr', 'French (fr)'),
248 ('fr', 'French (fr)'),
249 ('it', 'Italian (it)'),
249 ('it', 'Italian (it)'),
250 ('ja', 'Japanese (ja)'),
250 ('ja', 'Japanese (ja)'),
251 ('pl', 'Polish (pl)'),
251 ('pl', 'Polish (pl)'),
252 ('pt', 'Portuguese (pt)'),
252 ('pt', 'Portuguese (pt)'),
253 ('ru', 'Russian (ru)'),
253 ('ru', 'Russian (ru)'),
254 ('zh', 'Chinese (zh)'),
254 ('zh', 'Chinese (zh)'),
255 ]
255 ]
256 req = self.request
256 req = self.request
257
257
258 c.available_permissions = req.registry.settings['available_permissions']
258 c.available_permissions = req.registry.settings['available_permissions']
259 PermissionModel().set_global_permission_choices(
259 PermissionModel().set_global_permission_choices(
260 c, gettext_translator=req.translate)
260 c, gettext_translator=req.translate)
261
261
262 return c
262 return c
263
263
264 @LoginRequired()
264 @LoginRequired()
265 @HasPermissionAllDecorator('hg.admin')
265 @HasPermissionAllDecorator('hg.admin')
266 @CSRFRequired()
266 @CSRFRequired()
267 @view_config(
267 @view_config(
268 route_name='user_update', request_method='POST',
268 route_name='user_update', request_method='POST',
269 renderer='rhodecode:templates/admin/users/user_edit.mako')
269 renderer='rhodecode:templates/admin/users/user_edit.mako')
270 def user_update(self):
270 def user_update(self):
271 _ = self.request.translate
271 _ = self.request.translate
272 c = self.load_default_context()
272 c = self.load_default_context()
273
273
274 user_id = self.db_user_id
274 user_id = self.db_user_id
275 c.user = self.db_user
275 c.user = self.db_user
276
276
277 c.active = 'profile'
277 c.active = 'profile'
278 c.extern_type = c.user.extern_type
278 c.extern_type = c.user.extern_type
279 c.extern_name = c.user.extern_name
279 c.extern_name = c.user.extern_name
280 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
280 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
281 available_languages = [x[0] for x in c.allowed_languages]
281 available_languages = [x[0] for x in c.allowed_languages]
282 _form = UserForm(self.request.translate, edit=True,
282 _form = UserForm(self.request.translate, edit=True,
283 available_languages=available_languages,
283 available_languages=available_languages,
284 old_data={'user_id': user_id,
284 old_data={'user_id': user_id,
285 'email': c.user.email})()
285 'email': c.user.email})()
286 form_result = {}
286 form_result = {}
287 old_values = c.user.get_api_data()
287 old_values = c.user.get_api_data()
288 try:
288 try:
289 form_result = _form.to_python(dict(self.request.POST))
289 form_result = _form.to_python(dict(self.request.POST))
290 skip_attrs = ['extern_type', 'extern_name']
290 skip_attrs = ['extern_type', 'extern_name']
291 # TODO: plugin should define if username can be updated
291 # TODO: plugin should define if username can be updated
292 if c.extern_type != "rhodecode":
292 if c.extern_type != "rhodecode":
293 # forbid updating username for external accounts
293 # forbid updating username for external accounts
294 skip_attrs.append('username')
294 skip_attrs.append('username')
295
295
296 UserModel().update_user(
296 UserModel().update_user(
297 user_id, skip_attrs=skip_attrs, **form_result)
297 user_id, skip_attrs=skip_attrs, **form_result)
298
298
299 audit_logger.store_web(
299 audit_logger.store_web(
300 'user.edit', action_data={'old_data': old_values},
300 'user.edit', action_data={'old_data': old_values},
301 user=c.rhodecode_user)
301 user=c.rhodecode_user)
302
302
303 Session().commit()
303 Session().commit()
304 h.flash(_('User updated successfully'), category='success')
304 h.flash(_('User updated successfully'), category='success')
305 except formencode.Invalid as errors:
305 except formencode.Invalid as errors:
306 data = render(
306 data = render(
307 'rhodecode:templates/admin/users/user_edit.mako',
307 'rhodecode:templates/admin/users/user_edit.mako',
308 self._get_template_context(c), self.request)
308 self._get_template_context(c), self.request)
309 html = formencode.htmlfill.render(
309 html = formencode.htmlfill.render(
310 data,
310 data,
311 defaults=errors.value,
311 defaults=errors.value,
312 errors=errors.error_dict or {},
312 errors=errors.error_dict or {},
313 prefix_error=False,
313 prefix_error=False,
314 encoding="UTF-8",
314 encoding="UTF-8",
315 force_defaults=False
315 force_defaults=False
316 )
316 )
317 return Response(html)
317 return Response(html)
318 except UserCreationError as e:
318 except UserCreationError as e:
319 h.flash(e, 'error')
319 h.flash(e, 'error')
320 except Exception:
320 except Exception:
321 log.exception("Exception updating user")
321 log.exception("Exception updating user")
322 h.flash(_('Error occurred during update of user %s')
322 h.flash(_('Error occurred during update of user %s')
323 % form_result.get('username'), category='error')
323 % form_result.get('username'), category='error')
324 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
324 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
325
325
326 @LoginRequired()
326 @LoginRequired()
327 @HasPermissionAllDecorator('hg.admin')
327 @HasPermissionAllDecorator('hg.admin')
328 @CSRFRequired()
328 @CSRFRequired()
329 @view_config(
329 @view_config(
330 route_name='user_delete', request_method='POST',
330 route_name='user_delete', request_method='POST',
331 renderer='rhodecode:templates/admin/users/user_edit.mako')
331 renderer='rhodecode:templates/admin/users/user_edit.mako')
332 def user_delete(self):
332 def user_delete(self):
333 _ = self.request.translate
333 _ = self.request.translate
334 c = self.load_default_context()
334 c = self.load_default_context()
335 c.user = self.db_user
335 c.user = self.db_user
336
336
337 _repos = c.user.repositories
337 _repos = c.user.repositories
338 _repo_groups = c.user.repository_groups
338 _repo_groups = c.user.repository_groups
339 _user_groups = c.user.user_groups
339 _user_groups = c.user.user_groups
340
340
341 handle_repos = None
341 handle_repos = None
342 handle_repo_groups = None
342 handle_repo_groups = None
343 handle_user_groups = None
343 handle_user_groups = None
344 # dummy call for flash of handle
344 # dummy call for flash of handle
345 set_handle_flash_repos = lambda: None
345 set_handle_flash_repos = lambda: None
346 set_handle_flash_repo_groups = lambda: None
346 set_handle_flash_repo_groups = lambda: None
347 set_handle_flash_user_groups = lambda: None
347 set_handle_flash_user_groups = lambda: None
348
348
349 if _repos and self.request.POST.get('user_repos'):
349 if _repos and self.request.POST.get('user_repos'):
350 do = self.request.POST['user_repos']
350 do = self.request.POST['user_repos']
351 if do == 'detach':
351 if do == 'detach':
352 handle_repos = 'detach'
352 handle_repos = 'detach'
353 set_handle_flash_repos = lambda: h.flash(
353 set_handle_flash_repos = lambda: h.flash(
354 _('Detached %s repositories') % len(_repos),
354 _('Detached %s repositories') % len(_repos),
355 category='success')
355 category='success')
356 elif do == 'delete':
356 elif do == 'delete':
357 handle_repos = 'delete'
357 handle_repos = 'delete'
358 set_handle_flash_repos = lambda: h.flash(
358 set_handle_flash_repos = lambda: h.flash(
359 _('Deleted %s repositories') % len(_repos),
359 _('Deleted %s repositories') % len(_repos),
360 category='success')
360 category='success')
361
361
362 if _repo_groups and self.request.POST.get('user_repo_groups'):
362 if _repo_groups and self.request.POST.get('user_repo_groups'):
363 do = self.request.POST['user_repo_groups']
363 do = self.request.POST['user_repo_groups']
364 if do == 'detach':
364 if do == 'detach':
365 handle_repo_groups = 'detach'
365 handle_repo_groups = 'detach'
366 set_handle_flash_repo_groups = lambda: h.flash(
366 set_handle_flash_repo_groups = lambda: h.flash(
367 _('Detached %s repository groups') % len(_repo_groups),
367 _('Detached %s repository groups') % len(_repo_groups),
368 category='success')
368 category='success')
369 elif do == 'delete':
369 elif do == 'delete':
370 handle_repo_groups = 'delete'
370 handle_repo_groups = 'delete'
371 set_handle_flash_repo_groups = lambda: h.flash(
371 set_handle_flash_repo_groups = lambda: h.flash(
372 _('Deleted %s repository groups') % len(_repo_groups),
372 _('Deleted %s repository groups') % len(_repo_groups),
373 category='success')
373 category='success')
374
374
375 if _user_groups and self.request.POST.get('user_user_groups'):
375 if _user_groups and self.request.POST.get('user_user_groups'):
376 do = self.request.POST['user_user_groups']
376 do = self.request.POST['user_user_groups']
377 if do == 'detach':
377 if do == 'detach':
378 handle_user_groups = 'detach'
378 handle_user_groups = 'detach'
379 set_handle_flash_user_groups = lambda: h.flash(
379 set_handle_flash_user_groups = lambda: h.flash(
380 _('Detached %s user groups') % len(_user_groups),
380 _('Detached %s user groups') % len(_user_groups),
381 category='success')
381 category='success')
382 elif do == 'delete':
382 elif do == 'delete':
383 handle_user_groups = 'delete'
383 handle_user_groups = 'delete'
384 set_handle_flash_user_groups = lambda: h.flash(
384 set_handle_flash_user_groups = lambda: h.flash(
385 _('Deleted %s user groups') % len(_user_groups),
385 _('Deleted %s user groups') % len(_user_groups),
386 category='success')
386 category='success')
387
387
388 old_values = c.user.get_api_data()
388 old_values = c.user.get_api_data()
389 try:
389 try:
390 UserModel().delete(c.user, handle_repos=handle_repos,
390 UserModel().delete(c.user, handle_repos=handle_repos,
391 handle_repo_groups=handle_repo_groups,
391 handle_repo_groups=handle_repo_groups,
392 handle_user_groups=handle_user_groups)
392 handle_user_groups=handle_user_groups)
393
393
394 audit_logger.store_web(
394 audit_logger.store_web(
395 'user.delete', action_data={'old_data': old_values},
395 'user.delete', action_data={'old_data': old_values},
396 user=c.rhodecode_user)
396 user=c.rhodecode_user)
397
397
398 Session().commit()
398 Session().commit()
399 set_handle_flash_repos()
399 set_handle_flash_repos()
400 set_handle_flash_repo_groups()
400 set_handle_flash_repo_groups()
401 set_handle_flash_user_groups()
401 set_handle_flash_user_groups()
402 h.flash(_('Successfully deleted user'), category='success')
402 h.flash(_('Successfully deleted user'), category='success')
403 except (UserOwnsReposException, UserOwnsRepoGroupsException,
403 except (UserOwnsReposException, UserOwnsRepoGroupsException,
404 UserOwnsUserGroupsException, DefaultUserException) as e:
404 UserOwnsUserGroupsException, DefaultUserException) as e:
405 h.flash(e, category='warning')
405 h.flash(e, category='warning')
406 except Exception:
406 except Exception:
407 log.exception("Exception during deletion of user")
407 log.exception("Exception during deletion of user")
408 h.flash(_('An error occurred during deletion of user'),
408 h.flash(_('An error occurred during deletion of user'),
409 category='error')
409 category='error')
410 raise HTTPFound(h.route_path('users'))
410 raise HTTPFound(h.route_path('users'))
411
411
412 @LoginRequired()
412 @LoginRequired()
413 @HasPermissionAllDecorator('hg.admin')
413 @HasPermissionAllDecorator('hg.admin')
414 @view_config(
414 @view_config(
415 route_name='user_edit', request_method='GET',
415 route_name='user_edit', request_method='GET',
416 renderer='rhodecode:templates/admin/users/user_edit.mako')
416 renderer='rhodecode:templates/admin/users/user_edit.mako')
417 def user_edit(self):
417 def user_edit(self):
418 _ = self.request.translate
418 _ = self.request.translate
419 c = self.load_default_context()
419 c = self.load_default_context()
420 c.user = self.db_user
420 c.user = self.db_user
421
421
422 c.active = 'profile'
422 c.active = 'profile'
423 c.extern_type = c.user.extern_type
423 c.extern_type = c.user.extern_type
424 c.extern_name = c.user.extern_name
424 c.extern_name = c.user.extern_name
425 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
425 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
426
426
427 defaults = c.user.get_dict()
427 defaults = c.user.get_dict()
428 defaults.update({'language': c.user.user_data.get('language')})
428 defaults.update({'language': c.user.user_data.get('language')})
429
429
430 data = render(
430 data = render(
431 'rhodecode:templates/admin/users/user_edit.mako',
431 'rhodecode:templates/admin/users/user_edit.mako',
432 self._get_template_context(c), self.request)
432 self._get_template_context(c), self.request)
433 html = formencode.htmlfill.render(
433 html = formencode.htmlfill.render(
434 data,
434 data,
435 defaults=defaults,
435 defaults=defaults,
436 encoding="UTF-8",
436 encoding="UTF-8",
437 force_defaults=False
437 force_defaults=False
438 )
438 )
439 return Response(html)
439 return Response(html)
440
440
441 @LoginRequired()
441 @LoginRequired()
442 @HasPermissionAllDecorator('hg.admin')
442 @HasPermissionAllDecorator('hg.admin')
443 @view_config(
443 @view_config(
444 route_name='user_edit_advanced', request_method='GET',
444 route_name='user_edit_advanced', request_method='GET',
445 renderer='rhodecode:templates/admin/users/user_edit.mako')
445 renderer='rhodecode:templates/admin/users/user_edit.mako')
446 def user_edit_advanced(self):
446 def user_edit_advanced(self):
447 _ = self.request.translate
447 _ = self.request.translate
448 c = self.load_default_context()
448 c = self.load_default_context()
449
449
450 user_id = self.db_user_id
450 user_id = self.db_user_id
451 c.user = self.db_user
451 c.user = self.db_user
452
452
453 c.active = 'advanced'
453 c.active = 'advanced'
454 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
454 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
455 c.personal_repo_group_name = RepoGroupModel()\
455 c.personal_repo_group_name = RepoGroupModel()\
456 .get_personal_group_name(c.user)
456 .get_personal_group_name(c.user)
457
457
458 c.user_to_review_rules = sorted(
458 c.user_to_review_rules = sorted(
459 (x.user for x in c.user.user_review_rules),
459 (x.user for x in c.user.user_review_rules),
460 key=lambda u: u.username.lower())
460 key=lambda u: u.username.lower())
461
461
462 c.first_admin = User.get_first_super_admin()
462 c.first_admin = User.get_first_super_admin()
463 defaults = c.user.get_dict()
463 defaults = c.user.get_dict()
464
464
465 # Interim workaround if the user participated on any pull requests as a
465 # Interim workaround if the user participated on any pull requests as a
466 # reviewer.
466 # reviewer.
467 has_review = len(c.user.reviewer_pull_requests)
467 has_review = len(c.user.reviewer_pull_requests)
468 c.can_delete_user = not has_review
468 c.can_delete_user = not has_review
469 c.can_delete_user_message = ''
469 c.can_delete_user_message = ''
470 inactive_link = h.link_to(
470 inactive_link = h.link_to(
471 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
471 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
472 if has_review == 1:
472 if has_review == 1:
473 c.can_delete_user_message = h.literal(_(
473 c.can_delete_user_message = h.literal(_(
474 'The user participates as reviewer in {} pull request and '
474 'The user participates as reviewer in {} pull request and '
475 'cannot be deleted. \nYou can set the user to '
475 'cannot be deleted. \nYou can set the user to '
476 '"{}" instead of deleting it.').format(
476 '"{}" instead of deleting it.').format(
477 has_review, inactive_link))
477 has_review, inactive_link))
478 elif has_review:
478 elif has_review:
479 c.can_delete_user_message = h.literal(_(
479 c.can_delete_user_message = h.literal(_(
480 'The user participates as reviewer in {} pull requests and '
480 'The user participates as reviewer in {} pull requests and '
481 'cannot be deleted. \nYou can set the user to '
481 'cannot be deleted. \nYou can set the user to '
482 '"{}" instead of deleting it.').format(
482 '"{}" instead of deleting it.').format(
483 has_review, inactive_link))
483 has_review, inactive_link))
484
484
485 data = render(
485 data = render(
486 'rhodecode:templates/admin/users/user_edit.mako',
486 'rhodecode:templates/admin/users/user_edit.mako',
487 self._get_template_context(c), self.request)
487 self._get_template_context(c), self.request)
488 html = formencode.htmlfill.render(
488 html = formencode.htmlfill.render(
489 data,
489 data,
490 defaults=defaults,
490 defaults=defaults,
491 encoding="UTF-8",
491 encoding="UTF-8",
492 force_defaults=False
492 force_defaults=False
493 )
493 )
494 return Response(html)
494 return Response(html)
495
495
496 @LoginRequired()
496 @LoginRequired()
497 @HasPermissionAllDecorator('hg.admin')
497 @HasPermissionAllDecorator('hg.admin')
498 @view_config(
498 @view_config(
499 route_name='user_edit_global_perms', request_method='GET',
499 route_name='user_edit_global_perms', request_method='GET',
500 renderer='rhodecode:templates/admin/users/user_edit.mako')
500 renderer='rhodecode:templates/admin/users/user_edit.mako')
501 def user_edit_global_perms(self):
501 def user_edit_global_perms(self):
502 _ = self.request.translate
502 _ = self.request.translate
503 c = self.load_default_context()
503 c = self.load_default_context()
504 c.user = self.db_user
504 c.user = self.db_user
505
505
506 c.active = 'global_perms'
506 c.active = 'global_perms'
507
507
508 c.default_user = User.get_default_user()
508 c.default_user = User.get_default_user()
509 defaults = c.user.get_dict()
509 defaults = c.user.get_dict()
510 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
510 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
511 defaults.update(c.default_user.get_default_perms())
511 defaults.update(c.default_user.get_default_perms())
512 defaults.update(c.user.get_default_perms())
512 defaults.update(c.user.get_default_perms())
513
513
514 data = render(
514 data = render(
515 'rhodecode:templates/admin/users/user_edit.mako',
515 'rhodecode:templates/admin/users/user_edit.mako',
516 self._get_template_context(c), self.request)
516 self._get_template_context(c), self.request)
517 html = formencode.htmlfill.render(
517 html = formencode.htmlfill.render(
518 data,
518 data,
519 defaults=defaults,
519 defaults=defaults,
520 encoding="UTF-8",
520 encoding="UTF-8",
521 force_defaults=False
521 force_defaults=False
522 )
522 )
523 return Response(html)
523 return Response(html)
524
524
525 @LoginRequired()
525 @LoginRequired()
526 @HasPermissionAllDecorator('hg.admin')
526 @HasPermissionAllDecorator('hg.admin')
527 @CSRFRequired()
527 @CSRFRequired()
528 @view_config(
528 @view_config(
529 route_name='user_edit_global_perms_update', request_method='POST',
529 route_name='user_edit_global_perms_update', request_method='POST',
530 renderer='rhodecode:templates/admin/users/user_edit.mako')
530 renderer='rhodecode:templates/admin/users/user_edit.mako')
531 def user_edit_global_perms_update(self):
531 def user_edit_global_perms_update(self):
532 _ = self.request.translate
532 _ = self.request.translate
533 c = self.load_default_context()
533 c = self.load_default_context()
534
534
535 user_id = self.db_user_id
535 user_id = self.db_user_id
536 c.user = self.db_user
536 c.user = self.db_user
537
537
538 c.active = 'global_perms'
538 c.active = 'global_perms'
539 try:
539 try:
540 # first stage that verifies the checkbox
540 # first stage that verifies the checkbox
541 _form = UserIndividualPermissionsForm(self.request.translate)
541 _form = UserIndividualPermissionsForm(self.request.translate)
542 form_result = _form.to_python(dict(self.request.POST))
542 form_result = _form.to_python(dict(self.request.POST))
543 inherit_perms = form_result['inherit_default_permissions']
543 inherit_perms = form_result['inherit_default_permissions']
544 c.user.inherit_default_permissions = inherit_perms
544 c.user.inherit_default_permissions = inherit_perms
545 Session().add(c.user)
545 Session().add(c.user)
546
546
547 if not inherit_perms:
547 if not inherit_perms:
548 # only update the individual ones if we un check the flag
548 # only update the individual ones if we un check the flag
549 _form = UserPermissionsForm(
549 _form = UserPermissionsForm(
550 self.request.translate,
550 self.request.translate,
551 [x[0] for x in c.repo_create_choices],
551 [x[0] for x in c.repo_create_choices],
552 [x[0] for x in c.repo_create_on_write_choices],
552 [x[0] for x in c.repo_create_on_write_choices],
553 [x[0] for x in c.repo_group_create_choices],
553 [x[0] for x in c.repo_group_create_choices],
554 [x[0] for x in c.user_group_create_choices],
554 [x[0] for x in c.user_group_create_choices],
555 [x[0] for x in c.fork_choices],
555 [x[0] for x in c.fork_choices],
556 [x[0] for x in c.inherit_default_permission_choices])()
556 [x[0] for x in c.inherit_default_permission_choices])()
557
557
558 form_result = _form.to_python(dict(self.request.POST))
558 form_result = _form.to_python(dict(self.request.POST))
559 form_result.update({'perm_user_id': c.user.user_id})
559 form_result.update({'perm_user_id': c.user.user_id})
560
560
561 PermissionModel().update_user_permissions(form_result)
561 PermissionModel().update_user_permissions(form_result)
562
562
563 # TODO(marcink): implement global permissions
563 # TODO(marcink): implement global permissions
564 # audit_log.store_web('user.edit.permissions')
564 # audit_log.store_web('user.edit.permissions')
565
565
566 Session().commit()
566 Session().commit()
567 h.flash(_('User global permissions updated successfully'),
567 h.flash(_('User global permissions updated successfully'),
568 category='success')
568 category='success')
569
569
570 except formencode.Invalid as errors:
570 except formencode.Invalid as errors:
571 data = render(
571 data = render(
572 'rhodecode:templates/admin/users/user_edit.mako',
572 'rhodecode:templates/admin/users/user_edit.mako',
573 self._get_template_context(c), self.request)
573 self._get_template_context(c), self.request)
574 html = formencode.htmlfill.render(
574 html = formencode.htmlfill.render(
575 data,
575 data,
576 defaults=errors.value,
576 defaults=errors.value,
577 errors=errors.error_dict or {},
577 errors=errors.error_dict or {},
578 prefix_error=False,
578 prefix_error=False,
579 encoding="UTF-8",
579 encoding="UTF-8",
580 force_defaults=False
580 force_defaults=False
581 )
581 )
582 return Response(html)
582 return Response(html)
583 except Exception:
583 except Exception:
584 log.exception("Exception during permissions saving")
584 log.exception("Exception during permissions saving")
585 h.flash(_('An error occurred during permissions saving'),
585 h.flash(_('An error occurred during permissions saving'),
586 category='error')
586 category='error')
587 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
587 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
588
588
589 @LoginRequired()
589 @LoginRequired()
590 @HasPermissionAllDecorator('hg.admin')
590 @HasPermissionAllDecorator('hg.admin')
591 @CSRFRequired()
591 @CSRFRequired()
592 @view_config(
592 @view_config(
593 route_name='user_force_password_reset', request_method='POST',
593 route_name='user_force_password_reset', request_method='POST',
594 renderer='rhodecode:templates/admin/users/user_edit.mako')
594 renderer='rhodecode:templates/admin/users/user_edit.mako')
595 def user_force_password_reset(self):
595 def user_force_password_reset(self):
596 """
596 """
597 toggle reset password flag for this user
597 toggle reset password flag for this user
598 """
598 """
599 _ = self.request.translate
599 _ = self.request.translate
600 c = self.load_default_context()
600 c = self.load_default_context()
601
601
602 user_id = self.db_user_id
602 user_id = self.db_user_id
603 c.user = self.db_user
603 c.user = self.db_user
604
604
605 try:
605 try:
606 old_value = c.user.user_data.get('force_password_change')
606 old_value = c.user.user_data.get('force_password_change')
607 c.user.update_userdata(force_password_change=not old_value)
607 c.user.update_userdata(force_password_change=not old_value)
608
608
609 if old_value:
609 if old_value:
610 msg = _('Force password change disabled for user')
610 msg = _('Force password change disabled for user')
611 audit_logger.store_web(
611 audit_logger.store_web(
612 'user.edit.password_reset.disabled',
612 'user.edit.password_reset.disabled',
613 user=c.rhodecode_user)
613 user=c.rhodecode_user)
614 else:
614 else:
615 msg = _('Force password change enabled for user')
615 msg = _('Force password change enabled for user')
616 audit_logger.store_web(
616 audit_logger.store_web(
617 'user.edit.password_reset.enabled',
617 'user.edit.password_reset.enabled',
618 user=c.rhodecode_user)
618 user=c.rhodecode_user)
619
619
620 Session().commit()
620 Session().commit()
621 h.flash(msg, category='success')
621 h.flash(msg, category='success')
622 except Exception:
622 except Exception:
623 log.exception("Exception during password reset for user")
623 log.exception("Exception during password reset for user")
624 h.flash(_('An error occurred during password reset for user'),
624 h.flash(_('An error occurred during password reset for user'),
625 category='error')
625 category='error')
626
626
627 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
627 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
628
628
629 @LoginRequired()
629 @LoginRequired()
630 @HasPermissionAllDecorator('hg.admin')
630 @HasPermissionAllDecorator('hg.admin')
631 @CSRFRequired()
631 @CSRFRequired()
632 @view_config(
632 @view_config(
633 route_name='user_create_personal_repo_group', request_method='POST',
633 route_name='user_create_personal_repo_group', request_method='POST',
634 renderer='rhodecode:templates/admin/users/user_edit.mako')
634 renderer='rhodecode:templates/admin/users/user_edit.mako')
635 def user_create_personal_repo_group(self):
635 def user_create_personal_repo_group(self):
636 """
636 """
637 Create personal repository group for this user
637 Create personal repository group for this user
638 """
638 """
639 from rhodecode.model.repo_group import RepoGroupModel
639 from rhodecode.model.repo_group import RepoGroupModel
640
640
641 _ = self.request.translate
641 _ = self.request.translate
642 c = self.load_default_context()
642 c = self.load_default_context()
643
643
644 user_id = self.db_user_id
644 user_id = self.db_user_id
645 c.user = self.db_user
645 c.user = self.db_user
646
646
647 personal_repo_group = RepoGroup.get_user_personal_repo_group(
647 personal_repo_group = RepoGroup.get_user_personal_repo_group(
648 c.user.user_id)
648 c.user.user_id)
649 if personal_repo_group:
649 if personal_repo_group:
650 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
650 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
651
651
652 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
652 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
653 c.user)
653 c.user)
654 named_personal_group = RepoGroup.get_by_group_name(
654 named_personal_group = RepoGroup.get_by_group_name(
655 personal_repo_group_name)
655 personal_repo_group_name)
656 try:
656 try:
657
657
658 if named_personal_group and named_personal_group.user_id == c.user.user_id:
658 if named_personal_group and named_personal_group.user_id == c.user.user_id:
659 # migrate the same named group, and mark it as personal
659 # migrate the same named group, and mark it as personal
660 named_personal_group.personal = True
660 named_personal_group.personal = True
661 Session().add(named_personal_group)
661 Session().add(named_personal_group)
662 Session().commit()
662 Session().commit()
663 msg = _('Linked repository group `%s` as personal' % (
663 msg = _('Linked repository group `%s` as personal' % (
664 personal_repo_group_name,))
664 personal_repo_group_name,))
665 h.flash(msg, category='success')
665 h.flash(msg, category='success')
666 elif not named_personal_group:
666 elif not named_personal_group:
667 RepoGroupModel().create_personal_repo_group(c.user)
667 RepoGroupModel().create_personal_repo_group(c.user)
668
668
669 msg = _('Created repository group `%s`' % (
669 msg = _('Created repository group `%s`' % (
670 personal_repo_group_name,))
670 personal_repo_group_name,))
671 h.flash(msg, category='success')
671 h.flash(msg, category='success')
672 else:
672 else:
673 msg = _('Repository group `%s` is already taken' % (
673 msg = _('Repository group `%s` is already taken' % (
674 personal_repo_group_name,))
674 personal_repo_group_name,))
675 h.flash(msg, category='warning')
675 h.flash(msg, category='warning')
676 except Exception:
676 except Exception:
677 log.exception("Exception during repository group creation")
677 log.exception("Exception during repository group creation")
678 msg = _(
678 msg = _(
679 'An error occurred during repository group creation for user')
679 'An error occurred during repository group creation for user')
680 h.flash(msg, category='error')
680 h.flash(msg, category='error')
681 Session().rollback()
681 Session().rollback()
682
682
683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
684
684
685 @LoginRequired()
685 @LoginRequired()
686 @HasPermissionAllDecorator('hg.admin')
686 @HasPermissionAllDecorator('hg.admin')
687 @view_config(
687 @view_config(
688 route_name='edit_user_auth_tokens', request_method='GET',
688 route_name='edit_user_auth_tokens', request_method='GET',
689 renderer='rhodecode:templates/admin/users/user_edit.mako')
689 renderer='rhodecode:templates/admin/users/user_edit.mako')
690 def auth_tokens(self):
690 def auth_tokens(self):
691 _ = self.request.translate
691 _ = self.request.translate
692 c = self.load_default_context()
692 c = self.load_default_context()
693 c.user = self.db_user
693 c.user = self.db_user
694
694
695 c.active = 'auth_tokens'
695 c.active = 'auth_tokens'
696
696
697 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
697 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
698 c.role_values = [
698 c.role_values = [
699 (x, AuthTokenModel.cls._get_role_name(x))
699 (x, AuthTokenModel.cls._get_role_name(x))
700 for x in AuthTokenModel.cls.ROLES]
700 for x in AuthTokenModel.cls.ROLES]
701 c.role_options = [(c.role_values, _("Role"))]
701 c.role_options = [(c.role_values, _("Role"))]
702 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
702 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
703 c.user.user_id, show_expired=True)
703 c.user.user_id, show_expired=True)
704 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
704 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
705 return self._get_template_context(c)
705 return self._get_template_context(c)
706
706
707 def maybe_attach_token_scope(self, token):
707 def maybe_attach_token_scope(self, token):
708 # implemented in EE edition
708 # implemented in EE edition
709 pass
709 pass
710
710
711 @LoginRequired()
711 @LoginRequired()
712 @HasPermissionAllDecorator('hg.admin')
712 @HasPermissionAllDecorator('hg.admin')
713 @CSRFRequired()
713 @CSRFRequired()
714 @view_config(
714 @view_config(
715 route_name='edit_user_auth_tokens_add', request_method='POST')
715 route_name='edit_user_auth_tokens_add', request_method='POST')
716 def auth_tokens_add(self):
716 def auth_tokens_add(self):
717 _ = self.request.translate
717 _ = self.request.translate
718 c = self.load_default_context()
718 c = self.load_default_context()
719
719
720 user_id = self.db_user_id
720 user_id = self.db_user_id
721 c.user = self.db_user
721 c.user = self.db_user
722
722
723 user_data = c.user.get_api_data()
723 user_data = c.user.get_api_data()
724 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
724 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
725 description = self.request.POST.get('description')
725 description = self.request.POST.get('description')
726 role = self.request.POST.get('role')
726 role = self.request.POST.get('role')
727
727
728 token = AuthTokenModel().create(
728 token = AuthTokenModel().create(
729 c.user.user_id, description, lifetime, role)
729 c.user.user_id, description, lifetime, role)
730 token_data = token.get_api_data()
730 token_data = token.get_api_data()
731
731
732 self.maybe_attach_token_scope(token)
732 self.maybe_attach_token_scope(token)
733 audit_logger.store_web(
733 audit_logger.store_web(
734 'user.edit.token.add', action_data={
734 'user.edit.token.add', action_data={
735 'data': {'token': token_data, 'user': user_data}},
735 'data': {'token': token_data, 'user': user_data}},
736 user=self._rhodecode_user, )
736 user=self._rhodecode_user, )
737 Session().commit()
737 Session().commit()
738
738
739 h.flash(_("Auth token successfully created"), category='success')
739 h.flash(_("Auth token successfully created"), category='success')
740 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
740 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
741
741
742 @LoginRequired()
742 @LoginRequired()
743 @HasPermissionAllDecorator('hg.admin')
743 @HasPermissionAllDecorator('hg.admin')
744 @CSRFRequired()
744 @CSRFRequired()
745 @view_config(
745 @view_config(
746 route_name='edit_user_auth_tokens_delete', request_method='POST')
746 route_name='edit_user_auth_tokens_delete', request_method='POST')
747 def auth_tokens_delete(self):
747 def auth_tokens_delete(self):
748 _ = self.request.translate
748 _ = self.request.translate
749 c = self.load_default_context()
749 c = self.load_default_context()
750
750
751 user_id = self.db_user_id
751 user_id = self.db_user_id
752 c.user = self.db_user
752 c.user = self.db_user
753
753
754 user_data = c.user.get_api_data()
754 user_data = c.user.get_api_data()
755
755
756 del_auth_token = self.request.POST.get('del_auth_token')
756 del_auth_token = self.request.POST.get('del_auth_token')
757
757
758 if del_auth_token:
758 if del_auth_token:
759 token = UserApiKeys.get_or_404(del_auth_token)
759 token = UserApiKeys.get_or_404(del_auth_token)
760 token_data = token.get_api_data()
760 token_data = token.get_api_data()
761
761
762 AuthTokenModel().delete(del_auth_token, c.user.user_id)
762 AuthTokenModel().delete(del_auth_token, c.user.user_id)
763 audit_logger.store_web(
763 audit_logger.store_web(
764 'user.edit.token.delete', action_data={
764 'user.edit.token.delete', action_data={
765 'data': {'token': token_data, 'user': user_data}},
765 'data': {'token': token_data, 'user': user_data}},
766 user=self._rhodecode_user,)
766 user=self._rhodecode_user,)
767 Session().commit()
767 Session().commit()
768 h.flash(_("Auth token successfully deleted"), category='success')
768 h.flash(_("Auth token successfully deleted"), category='success')
769
769
770 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
770 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
771
771
772 @LoginRequired()
772 @LoginRequired()
773 @HasPermissionAllDecorator('hg.admin')
773 @HasPermissionAllDecorator('hg.admin')
774 @view_config(
774 @view_config(
775 route_name='edit_user_ssh_keys', request_method='GET',
775 route_name='edit_user_ssh_keys', request_method='GET',
776 renderer='rhodecode:templates/admin/users/user_edit.mako')
776 renderer='rhodecode:templates/admin/users/user_edit.mako')
777 def ssh_keys(self):
777 def ssh_keys(self):
778 _ = self.request.translate
778 _ = self.request.translate
779 c = self.load_default_context()
779 c = self.load_default_context()
780 c.user = self.db_user
780 c.user = self.db_user
781
781
782 c.active = 'ssh_keys'
782 c.active = 'ssh_keys'
783 c.default_key = self.request.GET.get('default_key')
783 c.default_key = self.request.GET.get('default_key')
784 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
784 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
785 return self._get_template_context(c)
785 return self._get_template_context(c)
786
786
787 @LoginRequired()
787 @LoginRequired()
788 @HasPermissionAllDecorator('hg.admin')
788 @HasPermissionAllDecorator('hg.admin')
789 @view_config(
789 @view_config(
790 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
790 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
791 renderer='rhodecode:templates/admin/users/user_edit.mako')
791 renderer='rhodecode:templates/admin/users/user_edit.mako')
792 def ssh_keys_generate_keypair(self):
792 def ssh_keys_generate_keypair(self):
793 _ = self.request.translate
793 _ = self.request.translate
794 c = self.load_default_context()
794 c = self.load_default_context()
795
795
796 c.user = self.db_user
796 c.user = self.db_user
797
797
798 c.active = 'ssh_keys_generate'
798 c.active = 'ssh_keys_generate'
799 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
799 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
800 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
800 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
801
801
802 return self._get_template_context(c)
802 return self._get_template_context(c)
803
803
804 @LoginRequired()
804 @LoginRequired()
805 @HasPermissionAllDecorator('hg.admin')
805 @HasPermissionAllDecorator('hg.admin')
806 @CSRFRequired()
806 @CSRFRequired()
807 @view_config(
807 @view_config(
808 route_name='edit_user_ssh_keys_add', request_method='POST')
808 route_name='edit_user_ssh_keys_add', request_method='POST')
809 def ssh_keys_add(self):
809 def ssh_keys_add(self):
810 _ = self.request.translate
810 _ = self.request.translate
811 c = self.load_default_context()
811 c = self.load_default_context()
812
812
813 user_id = self.db_user_id
813 user_id = self.db_user_id
814 c.user = self.db_user
814 c.user = self.db_user
815
815
816 user_data = c.user.get_api_data()
816 user_data = c.user.get_api_data()
817 key_data = self.request.POST.get('key_data')
817 key_data = self.request.POST.get('key_data')
818 description = self.request.POST.get('description')
818 description = self.request.POST.get('description')
819
819
820 fingerprint = 'unknown'
820 try:
821 try:
821 if not key_data:
822 if not key_data:
822 raise ValueError('Please add a valid public key')
823 raise ValueError('Please add a valid public key')
823
824
824 key = SshKeyModel().parse_key(key_data.strip())
825 key = SshKeyModel().parse_key(key_data.strip())
825 fingerprint = key.hash_md5()
826 fingerprint = key.hash_md5()
826
827
827 ssh_key = SshKeyModel().create(
828 ssh_key = SshKeyModel().create(
828 c.user.user_id, fingerprint, key_data, description)
829 c.user.user_id, fingerprint, key_data, description)
829 ssh_key_data = ssh_key.get_api_data()
830 ssh_key_data = ssh_key.get_api_data()
830
831
831 audit_logger.store_web(
832 audit_logger.store_web(
832 'user.edit.ssh_key.add', action_data={
833 'user.edit.ssh_key.add', action_data={
833 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
834 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
834 user=self._rhodecode_user, )
835 user=self._rhodecode_user, )
835 Session().commit()
836 Session().commit()
836
837
837 # Trigger an event on change of keys.
838 # Trigger an event on change of keys.
838 trigger(SshKeyFileChangeEvent(), self.request.registry)
839 trigger(SshKeyFileChangeEvent(), self.request.registry)
839
840
840 h.flash(_("Ssh Key successfully created"), category='success')
841 h.flash(_("Ssh Key successfully created"), category='success')
841
842
842 except IntegrityError:
843 except IntegrityError:
843 log.exception("Exception during ssh key saving")
844 log.exception("Exception during ssh key saving")
844 h.flash(_('An error occurred during ssh key saving: {}').format(
845 err = 'Such key with fingerprint `{}` already exists, ' \
845 'Such key already exists, please use a different one'),
846 'please use a different one'.format(fingerprint)
847 h.flash(_('An error occurred during ssh key saving: {}').format(err),
846 category='error')
848 category='error')
847 except Exception as e:
849 except Exception as e:
848 log.exception("Exception during ssh key saving")
850 log.exception("Exception during ssh key saving")
849 h.flash(_('An error occurred during ssh key saving: {}').format(e),
851 h.flash(_('An error occurred during ssh key saving: {}').format(e),
850 category='error')
852 category='error')
851
853
852 return HTTPFound(
854 return HTTPFound(
853 h.route_path('edit_user_ssh_keys', user_id=user_id))
855 h.route_path('edit_user_ssh_keys', user_id=user_id))
854
856
855 @LoginRequired()
857 @LoginRequired()
856 @HasPermissionAllDecorator('hg.admin')
858 @HasPermissionAllDecorator('hg.admin')
857 @CSRFRequired()
859 @CSRFRequired()
858 @view_config(
860 @view_config(
859 route_name='edit_user_ssh_keys_delete', request_method='POST')
861 route_name='edit_user_ssh_keys_delete', request_method='POST')
860 def ssh_keys_delete(self):
862 def ssh_keys_delete(self):
861 _ = self.request.translate
863 _ = self.request.translate
862 c = self.load_default_context()
864 c = self.load_default_context()
863
865
864 user_id = self.db_user_id
866 user_id = self.db_user_id
865 c.user = self.db_user
867 c.user = self.db_user
866
868
867 user_data = c.user.get_api_data()
869 user_data = c.user.get_api_data()
868
870
869 del_ssh_key = self.request.POST.get('del_ssh_key')
871 del_ssh_key = self.request.POST.get('del_ssh_key')
870
872
871 if del_ssh_key:
873 if del_ssh_key:
872 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
874 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
873 ssh_key_data = ssh_key.get_api_data()
875 ssh_key_data = ssh_key.get_api_data()
874
876
875 SshKeyModel().delete(del_ssh_key, c.user.user_id)
877 SshKeyModel().delete(del_ssh_key, c.user.user_id)
876 audit_logger.store_web(
878 audit_logger.store_web(
877 'user.edit.ssh_key.delete', action_data={
879 'user.edit.ssh_key.delete', action_data={
878 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
880 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
879 user=self._rhodecode_user,)
881 user=self._rhodecode_user,)
880 Session().commit()
882 Session().commit()
881 # Trigger an event on change of keys.
883 # Trigger an event on change of keys.
882 trigger(SshKeyFileChangeEvent(), self.request.registry)
884 trigger(SshKeyFileChangeEvent(), self.request.registry)
883 h.flash(_("Ssh key successfully deleted"), category='success')
885 h.flash(_("Ssh key successfully deleted"), category='success')
884
886
885 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
887 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
886
888
887 @LoginRequired()
889 @LoginRequired()
888 @HasPermissionAllDecorator('hg.admin')
890 @HasPermissionAllDecorator('hg.admin')
889 @view_config(
891 @view_config(
890 route_name='edit_user_emails', request_method='GET',
892 route_name='edit_user_emails', request_method='GET',
891 renderer='rhodecode:templates/admin/users/user_edit.mako')
893 renderer='rhodecode:templates/admin/users/user_edit.mako')
892 def emails(self):
894 def emails(self):
893 _ = self.request.translate
895 _ = self.request.translate
894 c = self.load_default_context()
896 c = self.load_default_context()
895 c.user = self.db_user
897 c.user = self.db_user
896
898
897 c.active = 'emails'
899 c.active = 'emails'
898 c.user_email_map = UserEmailMap.query() \
900 c.user_email_map = UserEmailMap.query() \
899 .filter(UserEmailMap.user == c.user).all()
901 .filter(UserEmailMap.user == c.user).all()
900
902
901 return self._get_template_context(c)
903 return self._get_template_context(c)
902
904
903 @LoginRequired()
905 @LoginRequired()
904 @HasPermissionAllDecorator('hg.admin')
906 @HasPermissionAllDecorator('hg.admin')
905 @CSRFRequired()
907 @CSRFRequired()
906 @view_config(
908 @view_config(
907 route_name='edit_user_emails_add', request_method='POST')
909 route_name='edit_user_emails_add', request_method='POST')
908 def emails_add(self):
910 def emails_add(self):
909 _ = self.request.translate
911 _ = self.request.translate
910 c = self.load_default_context()
912 c = self.load_default_context()
911
913
912 user_id = self.db_user_id
914 user_id = self.db_user_id
913 c.user = self.db_user
915 c.user = self.db_user
914
916
915 email = self.request.POST.get('new_email')
917 email = self.request.POST.get('new_email')
916 user_data = c.user.get_api_data()
918 user_data = c.user.get_api_data()
917 try:
919 try:
918
920
919 form = UserExtraEmailForm(self.request.translate)()
921 form = UserExtraEmailForm(self.request.translate)()
920 data = form.to_python({'email': email})
922 data = form.to_python({'email': email})
921 email = data['email']
923 email = data['email']
922
924
923 UserModel().add_extra_email(c.user.user_id, email)
925 UserModel().add_extra_email(c.user.user_id, email)
924 audit_logger.store_web(
926 audit_logger.store_web(
925 'user.edit.email.add',
927 'user.edit.email.add',
926 action_data={'email': email, 'user': user_data},
928 action_data={'email': email, 'user': user_data},
927 user=self._rhodecode_user)
929 user=self._rhodecode_user)
928 Session().commit()
930 Session().commit()
929 h.flash(_("Added new email address `%s` for user account") % email,
931 h.flash(_("Added new email address `%s` for user account") % email,
930 category='success')
932 category='success')
931 except formencode.Invalid as error:
933 except formencode.Invalid as error:
932 h.flash(h.escape(error.error_dict['email']), category='error')
934 h.flash(h.escape(error.error_dict['email']), category='error')
933 except IntegrityError:
935 except IntegrityError:
934 log.warning("Email %s already exists", email)
936 log.warning("Email %s already exists", email)
935 h.flash(_('Email `{}` is already registered for another user.').format(email),
937 h.flash(_('Email `{}` is already registered for another user.').format(email),
936 category='error')
938 category='error')
937 except Exception:
939 except Exception:
938 log.exception("Exception during email saving")
940 log.exception("Exception during email saving")
939 h.flash(_('An error occurred during email saving'),
941 h.flash(_('An error occurred during email saving'),
940 category='error')
942 category='error')
941 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
943 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
942
944
943 @LoginRequired()
945 @LoginRequired()
944 @HasPermissionAllDecorator('hg.admin')
946 @HasPermissionAllDecorator('hg.admin')
945 @CSRFRequired()
947 @CSRFRequired()
946 @view_config(
948 @view_config(
947 route_name='edit_user_emails_delete', request_method='POST')
949 route_name='edit_user_emails_delete', request_method='POST')
948 def emails_delete(self):
950 def emails_delete(self):
949 _ = self.request.translate
951 _ = self.request.translate
950 c = self.load_default_context()
952 c = self.load_default_context()
951
953
952 user_id = self.db_user_id
954 user_id = self.db_user_id
953 c.user = self.db_user
955 c.user = self.db_user
954
956
955 email_id = self.request.POST.get('del_email_id')
957 email_id = self.request.POST.get('del_email_id')
956 user_model = UserModel()
958 user_model = UserModel()
957
959
958 email = UserEmailMap.query().get(email_id).email
960 email = UserEmailMap.query().get(email_id).email
959 user_data = c.user.get_api_data()
961 user_data = c.user.get_api_data()
960 user_model.delete_extra_email(c.user.user_id, email_id)
962 user_model.delete_extra_email(c.user.user_id, email_id)
961 audit_logger.store_web(
963 audit_logger.store_web(
962 'user.edit.email.delete',
964 'user.edit.email.delete',
963 action_data={'email': email, 'user': user_data},
965 action_data={'email': email, 'user': user_data},
964 user=self._rhodecode_user)
966 user=self._rhodecode_user)
965 Session().commit()
967 Session().commit()
966 h.flash(_("Removed email address from user account"),
968 h.flash(_("Removed email address from user account"),
967 category='success')
969 category='success')
968 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
970 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
969
971
970 @LoginRequired()
972 @LoginRequired()
971 @HasPermissionAllDecorator('hg.admin')
973 @HasPermissionAllDecorator('hg.admin')
972 @view_config(
974 @view_config(
973 route_name='edit_user_ips', request_method='GET',
975 route_name='edit_user_ips', request_method='GET',
974 renderer='rhodecode:templates/admin/users/user_edit.mako')
976 renderer='rhodecode:templates/admin/users/user_edit.mako')
975 def ips(self):
977 def ips(self):
976 _ = self.request.translate
978 _ = self.request.translate
977 c = self.load_default_context()
979 c = self.load_default_context()
978 c.user = self.db_user
980 c.user = self.db_user
979
981
980 c.active = 'ips'
982 c.active = 'ips'
981 c.user_ip_map = UserIpMap.query() \
983 c.user_ip_map = UserIpMap.query() \
982 .filter(UserIpMap.user == c.user).all()
984 .filter(UserIpMap.user == c.user).all()
983
985
984 c.inherit_default_ips = c.user.inherit_default_permissions
986 c.inherit_default_ips = c.user.inherit_default_permissions
985 c.default_user_ip_map = UserIpMap.query() \
987 c.default_user_ip_map = UserIpMap.query() \
986 .filter(UserIpMap.user == User.get_default_user()).all()
988 .filter(UserIpMap.user == User.get_default_user()).all()
987
989
988 return self._get_template_context(c)
990 return self._get_template_context(c)
989
991
990 @LoginRequired()
992 @LoginRequired()
991 @HasPermissionAllDecorator('hg.admin')
993 @HasPermissionAllDecorator('hg.admin')
992 @CSRFRequired()
994 @CSRFRequired()
993 @view_config(
995 @view_config(
994 route_name='edit_user_ips_add', request_method='POST')
996 route_name='edit_user_ips_add', request_method='POST')
995 # NOTE(marcink): this view is allowed for default users, as we can
997 # NOTE(marcink): this view is allowed for default users, as we can
996 # edit their IP white list
998 # edit their IP white list
997 def ips_add(self):
999 def ips_add(self):
998 _ = self.request.translate
1000 _ = self.request.translate
999 c = self.load_default_context()
1001 c = self.load_default_context()
1000
1002
1001 user_id = self.db_user_id
1003 user_id = self.db_user_id
1002 c.user = self.db_user
1004 c.user = self.db_user
1003
1005
1004 user_model = UserModel()
1006 user_model = UserModel()
1005 desc = self.request.POST.get('description')
1007 desc = self.request.POST.get('description')
1006 try:
1008 try:
1007 ip_list = user_model.parse_ip_range(
1009 ip_list = user_model.parse_ip_range(
1008 self.request.POST.get('new_ip'))
1010 self.request.POST.get('new_ip'))
1009 except Exception as e:
1011 except Exception as e:
1010 ip_list = []
1012 ip_list = []
1011 log.exception("Exception during ip saving")
1013 log.exception("Exception during ip saving")
1012 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1014 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1013 category='error')
1015 category='error')
1014 added = []
1016 added = []
1015 user_data = c.user.get_api_data()
1017 user_data = c.user.get_api_data()
1016 for ip in ip_list:
1018 for ip in ip_list:
1017 try:
1019 try:
1018 form = UserExtraIpForm(self.request.translate)()
1020 form = UserExtraIpForm(self.request.translate)()
1019 data = form.to_python({'ip': ip})
1021 data = form.to_python({'ip': ip})
1020 ip = data['ip']
1022 ip = data['ip']
1021
1023
1022 user_model.add_extra_ip(c.user.user_id, ip, desc)
1024 user_model.add_extra_ip(c.user.user_id, ip, desc)
1023 audit_logger.store_web(
1025 audit_logger.store_web(
1024 'user.edit.ip.add',
1026 'user.edit.ip.add',
1025 action_data={'ip': ip, 'user': user_data},
1027 action_data={'ip': ip, 'user': user_data},
1026 user=self._rhodecode_user)
1028 user=self._rhodecode_user)
1027 Session().commit()
1029 Session().commit()
1028 added.append(ip)
1030 added.append(ip)
1029 except formencode.Invalid as error:
1031 except formencode.Invalid as error:
1030 msg = error.error_dict['ip']
1032 msg = error.error_dict['ip']
1031 h.flash(msg, category='error')
1033 h.flash(msg, category='error')
1032 except Exception:
1034 except Exception:
1033 log.exception("Exception during ip saving")
1035 log.exception("Exception during ip saving")
1034 h.flash(_('An error occurred during ip saving'),
1036 h.flash(_('An error occurred during ip saving'),
1035 category='error')
1037 category='error')
1036 if added:
1038 if added:
1037 h.flash(
1039 h.flash(
1038 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1040 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1039 category='success')
1041 category='success')
1040 if 'default_user' in self.request.POST:
1042 if 'default_user' in self.request.POST:
1041 # case for editing global IP list we do it for 'DEFAULT' user
1043 # case for editing global IP list we do it for 'DEFAULT' user
1042 raise HTTPFound(h.route_path('admin_permissions_ips'))
1044 raise HTTPFound(h.route_path('admin_permissions_ips'))
1043 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1045 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1044
1046
1045 @LoginRequired()
1047 @LoginRequired()
1046 @HasPermissionAllDecorator('hg.admin')
1048 @HasPermissionAllDecorator('hg.admin')
1047 @CSRFRequired()
1049 @CSRFRequired()
1048 @view_config(
1050 @view_config(
1049 route_name='edit_user_ips_delete', request_method='POST')
1051 route_name='edit_user_ips_delete', request_method='POST')
1050 # NOTE(marcink): this view is allowed for default users, as we can
1052 # NOTE(marcink): this view is allowed for default users, as we can
1051 # edit their IP white list
1053 # edit their IP white list
1052 def ips_delete(self):
1054 def ips_delete(self):
1053 _ = self.request.translate
1055 _ = self.request.translate
1054 c = self.load_default_context()
1056 c = self.load_default_context()
1055
1057
1056 user_id = self.db_user_id
1058 user_id = self.db_user_id
1057 c.user = self.db_user
1059 c.user = self.db_user
1058
1060
1059 ip_id = self.request.POST.get('del_ip_id')
1061 ip_id = self.request.POST.get('del_ip_id')
1060 user_model = UserModel()
1062 user_model = UserModel()
1061 user_data = c.user.get_api_data()
1063 user_data = c.user.get_api_data()
1062 ip = UserIpMap.query().get(ip_id).ip_addr
1064 ip = UserIpMap.query().get(ip_id).ip_addr
1063 user_model.delete_extra_ip(c.user.user_id, ip_id)
1065 user_model.delete_extra_ip(c.user.user_id, ip_id)
1064 audit_logger.store_web(
1066 audit_logger.store_web(
1065 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1067 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1066 user=self._rhodecode_user)
1068 user=self._rhodecode_user)
1067 Session().commit()
1069 Session().commit()
1068 h.flash(_("Removed ip address from user whitelist"), category='success')
1070 h.flash(_("Removed ip address from user whitelist"), category='success')
1069
1071
1070 if 'default_user' in self.request.POST:
1072 if 'default_user' in self.request.POST:
1071 # case for editing global IP list we do it for 'DEFAULT' user
1073 # case for editing global IP list we do it for 'DEFAULT' user
1072 raise HTTPFound(h.route_path('admin_permissions_ips'))
1074 raise HTTPFound(h.route_path('admin_permissions_ips'))
1073 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1075 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1074
1076
1075 @LoginRequired()
1077 @LoginRequired()
1076 @HasPermissionAllDecorator('hg.admin')
1078 @HasPermissionAllDecorator('hg.admin')
1077 @view_config(
1079 @view_config(
1078 route_name='edit_user_groups_management', request_method='GET',
1080 route_name='edit_user_groups_management', request_method='GET',
1079 renderer='rhodecode:templates/admin/users/user_edit.mako')
1081 renderer='rhodecode:templates/admin/users/user_edit.mako')
1080 def groups_management(self):
1082 def groups_management(self):
1081 c = self.load_default_context()
1083 c = self.load_default_context()
1082 c.user = self.db_user
1084 c.user = self.db_user
1083 c.data = c.user.group_member
1085 c.data = c.user.group_member
1084
1086
1085 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1087 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1086 for group in c.user.group_member]
1088 for group in c.user.group_member]
1087 c.groups = json.dumps(groups)
1089 c.groups = json.dumps(groups)
1088 c.active = 'groups'
1090 c.active = 'groups'
1089
1091
1090 return self._get_template_context(c)
1092 return self._get_template_context(c)
1091
1093
1092 @LoginRequired()
1094 @LoginRequired()
1093 @HasPermissionAllDecorator('hg.admin')
1095 @HasPermissionAllDecorator('hg.admin')
1094 @CSRFRequired()
1096 @CSRFRequired()
1095 @view_config(
1097 @view_config(
1096 route_name='edit_user_groups_management_updates', request_method='POST')
1098 route_name='edit_user_groups_management_updates', request_method='POST')
1097 def groups_management_updates(self):
1099 def groups_management_updates(self):
1098 _ = self.request.translate
1100 _ = self.request.translate
1099 c = self.load_default_context()
1101 c = self.load_default_context()
1100
1102
1101 user_id = self.db_user_id
1103 user_id = self.db_user_id
1102 c.user = self.db_user
1104 c.user = self.db_user
1103
1105
1104 user_groups = set(self.request.POST.getall('users_group_id'))
1106 user_groups = set(self.request.POST.getall('users_group_id'))
1105 user_groups_objects = []
1107 user_groups_objects = []
1106
1108
1107 for ugid in user_groups:
1109 for ugid in user_groups:
1108 user_groups_objects.append(
1110 user_groups_objects.append(
1109 UserGroupModel().get_group(safe_int(ugid)))
1111 UserGroupModel().get_group(safe_int(ugid)))
1110 user_group_model = UserGroupModel()
1112 user_group_model = UserGroupModel()
1111 added_to_groups, removed_from_groups = \
1113 added_to_groups, removed_from_groups = \
1112 user_group_model.change_groups(c.user, user_groups_objects)
1114 user_group_model.change_groups(c.user, user_groups_objects)
1113
1115
1114 user_data = c.user.get_api_data()
1116 user_data = c.user.get_api_data()
1115 for user_group_id in added_to_groups:
1117 for user_group_id in added_to_groups:
1116 user_group = UserGroup.get(user_group_id)
1118 user_group = UserGroup.get(user_group_id)
1117 old_values = user_group.get_api_data()
1119 old_values = user_group.get_api_data()
1118 audit_logger.store_web(
1120 audit_logger.store_web(
1119 'user_group.edit.member.add',
1121 'user_group.edit.member.add',
1120 action_data={'user': user_data, 'old_data': old_values},
1122 action_data={'user': user_data, 'old_data': old_values},
1121 user=self._rhodecode_user)
1123 user=self._rhodecode_user)
1122
1124
1123 for user_group_id in removed_from_groups:
1125 for user_group_id in removed_from_groups:
1124 user_group = UserGroup.get(user_group_id)
1126 user_group = UserGroup.get(user_group_id)
1125 old_values = user_group.get_api_data()
1127 old_values = user_group.get_api_data()
1126 audit_logger.store_web(
1128 audit_logger.store_web(
1127 'user_group.edit.member.delete',
1129 'user_group.edit.member.delete',
1128 action_data={'user': user_data, 'old_data': old_values},
1130 action_data={'user': user_data, 'old_data': old_values},
1129 user=self._rhodecode_user)
1131 user=self._rhodecode_user)
1130
1132
1131 Session().commit()
1133 Session().commit()
1132 c.active = 'user_groups_management'
1134 c.active = 'user_groups_management'
1133 h.flash(_("Groups successfully changed"), category='success')
1135 h.flash(_("Groups successfully changed"), category='success')
1134
1136
1135 return HTTPFound(h.route_path(
1137 return HTTPFound(h.route_path(
1136 'edit_user_groups_management', user_id=user_id))
1138 'edit_user_groups_management', user_id=user_id))
1137
1139
1138 @LoginRequired()
1140 @LoginRequired()
1139 @HasPermissionAllDecorator('hg.admin')
1141 @HasPermissionAllDecorator('hg.admin')
1140 @view_config(
1142 @view_config(
1141 route_name='edit_user_audit_logs', request_method='GET',
1143 route_name='edit_user_audit_logs', request_method='GET',
1142 renderer='rhodecode:templates/admin/users/user_edit.mako')
1144 renderer='rhodecode:templates/admin/users/user_edit.mako')
1143 def user_audit_logs(self):
1145 def user_audit_logs(self):
1144 _ = self.request.translate
1146 _ = self.request.translate
1145 c = self.load_default_context()
1147 c = self.load_default_context()
1146 c.user = self.db_user
1148 c.user = self.db_user
1147
1149
1148 c.active = 'audit'
1150 c.active = 'audit'
1149
1151
1150 p = safe_int(self.request.GET.get('page', 1), 1)
1152 p = safe_int(self.request.GET.get('page', 1), 1)
1151
1153
1152 filter_term = self.request.GET.get('filter')
1154 filter_term = self.request.GET.get('filter')
1153 user_log = UserModel().get_user_log(c.user, filter_term)
1155 user_log = UserModel().get_user_log(c.user, filter_term)
1154
1156
1155 def url_generator(**kw):
1157 def url_generator(**kw):
1156 if filter_term:
1158 if filter_term:
1157 kw['filter'] = filter_term
1159 kw['filter'] = filter_term
1158 return self.request.current_route_path(_query=kw)
1160 return self.request.current_route_path(_query=kw)
1159
1161
1160 c.audit_logs = h.Page(
1162 c.audit_logs = h.Page(
1161 user_log, page=p, items_per_page=10, url=url_generator)
1163 user_log, page=p, items_per_page=10, url=url_generator)
1162 c.filter_term = filter_term
1164 c.filter_term = filter_term
1163 return self._get_template_context(c)
1165 return self._get_template_context(c)
1164
1166
1165 @LoginRequired()
1167 @LoginRequired()
1166 @HasPermissionAllDecorator('hg.admin')
1168 @HasPermissionAllDecorator('hg.admin')
1167 @view_config(
1169 @view_config(
1168 route_name='edit_user_perms_summary', request_method='GET',
1170 route_name='edit_user_perms_summary', request_method='GET',
1169 renderer='rhodecode:templates/admin/users/user_edit.mako')
1171 renderer='rhodecode:templates/admin/users/user_edit.mako')
1170 def user_perms_summary(self):
1172 def user_perms_summary(self):
1171 _ = self.request.translate
1173 _ = self.request.translate
1172 c = self.load_default_context()
1174 c = self.load_default_context()
1173 c.user = self.db_user
1175 c.user = self.db_user
1174
1176
1175 c.active = 'perms_summary'
1177 c.active = 'perms_summary'
1176 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1178 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1177
1179
1178 return self._get_template_context(c)
1180 return self._get_template_context(c)
1179
1181
1180 @LoginRequired()
1182 @LoginRequired()
1181 @HasPermissionAllDecorator('hg.admin')
1183 @HasPermissionAllDecorator('hg.admin')
1182 @view_config(
1184 @view_config(
1183 route_name='edit_user_perms_summary_json', request_method='GET',
1185 route_name='edit_user_perms_summary_json', request_method='GET',
1184 renderer='json_ext')
1186 renderer='json_ext')
1185 def user_perms_summary_json(self):
1187 def user_perms_summary_json(self):
1186 self.load_default_context()
1188 self.load_default_context()
1187 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1189 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1188
1190
1189 return perm_user.permissions
1191 return perm_user.permissions
@@ -1,160 +1,163 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.model.db import User, UserSshKeys
23 from rhodecode.model.db import User, UserSshKeys
24
24
25 from rhodecode.tests import TestController, assert_session_flash
25 from rhodecode.tests import TestController, assert_session_flash
26 from rhodecode.tests.fixture import Fixture
26 from rhodecode.tests.fixture import Fixture
27
27
28 fixture = Fixture()
28 fixture = Fixture()
29
29
30
30
31 def route_path(name, params=None, **kwargs):
31 def route_path(name, params=None, **kwargs):
32 import urllib
32 import urllib
33 from rhodecode.apps._base import ADMIN_PREFIX
33 from rhodecode.apps._base import ADMIN_PREFIX
34
34
35 base_url = {
35 base_url = {
36 'my_account_ssh_keys':
36 'my_account_ssh_keys':
37 ADMIN_PREFIX + '/my_account/ssh_keys',
37 ADMIN_PREFIX + '/my_account/ssh_keys',
38 'my_account_ssh_keys_generate':
38 'my_account_ssh_keys_generate':
39 ADMIN_PREFIX + '/my_account/ssh_keys/generate',
39 ADMIN_PREFIX + '/my_account/ssh_keys/generate',
40 'my_account_ssh_keys_add':
40 'my_account_ssh_keys_add':
41 ADMIN_PREFIX + '/my_account/ssh_keys/new',
41 ADMIN_PREFIX + '/my_account/ssh_keys/new',
42 'my_account_ssh_keys_delete':
42 'my_account_ssh_keys_delete':
43 ADMIN_PREFIX + '/my_account/ssh_keys/delete',
43 ADMIN_PREFIX + '/my_account/ssh_keys/delete',
44 }[name].format(**kwargs)
44 }[name].format(**kwargs)
45
45
46 if params:
46 if params:
47 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
47 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
48 return base_url
48 return base_url
49
49
50
50
51 class TestMyAccountSshKeysView(TestController):
51 class TestMyAccountSshKeysView(TestController):
52 INVALID_KEY = """\
52 INVALID_KEY = """\
53 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vevJsuZds1iNU5
53 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vevJsuZds1iNU5
54 LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSykfR1D1TdluyIpQLrwgH5kb
54 LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSykfR1D1TdluyIpQLrwgH5kb
55 n8FkVI8zBMCKakxowvN67B0R7b1BT4PPzW2JlOXei/m9W12ZY484VTow6/B+kf2Q8
55 n8FkVI8zBMCKakxowvN67B0R7b1BT4PPzW2JlOXei/m9W12ZY484VTow6/B+kf2Q8
56 cP8tmCJmKWZma5Em7OTUhvjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6
56 cP8tmCJmKWZma5Em7OTUhvjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6
57 jvdphZTc30I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zP
57 jvdphZTc30I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zP
58 qPFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL
58 qPFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL
59 your_email@example.com
59 your_email@example.com
60 """
60 """
61 VALID_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vev' \
61 VALID_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vev' \
62 'JsuZds1iNU5LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSy' \
62 'JsuZds1iNU5LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSy' \
63 'kfR1D1TdluyIpQLrwgH5kbn8FkVI8zBMCKakxowvN67B0R7b1BT4PP' \
63 'kfR1D1TdluyIpQLrwgH5kbn8FkVI8zBMCKakxowvN67B0R7b1BT4PP' \
64 'zW2JlOXei/m9W12ZY484VTow6/B+kf2Q8cP8tmCJmKWZma5Em7OTUh' \
64 'zW2JlOXei/m9W12ZY484VTow6/B+kf2Q8cP8tmCJmKWZma5Em7OTUh' \
65 'vjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6jvdphZTc30' \
65 'vjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6jvdphZTc30' \
66 'I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zPq' \
66 'I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zPq' \
67 'PFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL ' \
67 'PFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL ' \
68 'your_email@example.com'
68 'your_email@example.com'
69 FINGERPRINT = 'MD5:01:4f:ad:29:22:6e:01:37:c9:d2:52:26:52:b0:2d:93'
69
70
70 def test_add_ssh_key_error(self, user_util):
71 def test_add_ssh_key_error(self, user_util):
71 user = user_util.create_user(password='qweqwe')
72 user = user_util.create_user(password='qweqwe')
72 self.log_user(user.username, 'qweqwe')
73 self.log_user(user.username, 'qweqwe')
73
74
74 key_data = self.INVALID_KEY
75 key_data = self.INVALID_KEY
75
76
76 desc = 'MY SSH KEY'
77 desc = 'MY SSH KEY'
77 response = self.app.post(
78 response = self.app.post(
78 route_path('my_account_ssh_keys_add'),
79 route_path('my_account_ssh_keys_add'),
79 {'description': desc, 'key_data': key_data,
80 {'description': desc, 'key_data': key_data,
80 'csrf_token': self.csrf_token})
81 'csrf_token': self.csrf_token})
81 assert_session_flash(response, 'An error occurred during ssh '
82 assert_session_flash(response, 'An error occurred during ssh '
82 'key saving: Unable to decode the key')
83 'key saving: Unable to decode the key')
83
84
84 def test_ssh_key_duplicate(self, user_util):
85 def test_ssh_key_duplicate(self, user_util):
85 user = user_util.create_user(password='qweqwe')
86 user = user_util.create_user(password='qweqwe')
86 self.log_user(user.username, 'qweqwe')
87 self.log_user(user.username, 'qweqwe')
87 key_data = self.VALID_KEY
88 key_data = self.VALID_KEY
88
89
89 desc = 'MY SSH KEY'
90 desc = 'MY SSH KEY'
90 response = self.app.post(
91 response = self.app.post(
91 route_path('my_account_ssh_keys_add'),
92 route_path('my_account_ssh_keys_add'),
92 {'description': desc, 'key_data': key_data,
93 {'description': desc, 'key_data': key_data,
93 'csrf_token': self.csrf_token})
94 'csrf_token': self.csrf_token})
94 assert_session_flash(response, 'Ssh Key successfully created')
95 assert_session_flash(response, 'Ssh Key successfully created')
95 response.follow() # flush session flash
96 response.follow() # flush session flash
96
97
97 # add the same key AGAIN
98 # add the same key AGAIN
98 desc = 'MY SSH KEY'
99 desc = 'MY SSH KEY'
99 response = self.app.post(
100 response = self.app.post(
100 route_path('my_account_ssh_keys_add'),
101 route_path('my_account_ssh_keys_add'),
101 {'description': desc, 'key_data': key_data,
102 {'description': desc, 'key_data': key_data,
102 'csrf_token': self.csrf_token})
103 'csrf_token': self.csrf_token})
104
105 err = 'Such key with fingerprint `{}` already exists, ' \
106 'please use a different one'.format(self.FINGERPRINT)
103 assert_session_flash(response, 'An error occurred during ssh key '
107 assert_session_flash(response, 'An error occurred during ssh key '
104 'saving: Such key already exists, '
108 'saving: {}'.format(err))
105 'please use a different one')
106
109
107 def test_add_ssh_key(self, user_util):
110 def test_add_ssh_key(self, user_util):
108 user = user_util.create_user(password='qweqwe')
111 user = user_util.create_user(password='qweqwe')
109 self.log_user(user.username, 'qweqwe')
112 self.log_user(user.username, 'qweqwe')
110
113
111 key_data = self.VALID_KEY
114 key_data = self.VALID_KEY
112
115
113 desc = 'MY SSH KEY'
116 desc = 'MY SSH KEY'
114 response = self.app.post(
117 response = self.app.post(
115 route_path('my_account_ssh_keys_add'),
118 route_path('my_account_ssh_keys_add'),
116 {'description': desc, 'key_data': key_data,
119 {'description': desc, 'key_data': key_data,
117 'csrf_token': self.csrf_token})
120 'csrf_token': self.csrf_token})
118 assert_session_flash(response, 'Ssh Key successfully created')
121 assert_session_flash(response, 'Ssh Key successfully created')
119
122
120 response = response.follow()
123 response = response.follow()
121 response.mustcontain(desc)
124 response.mustcontain(desc)
122
125
123 def test_delete_ssh_key(self, user_util):
126 def test_delete_ssh_key(self, user_util):
124 user = user_util.create_user(password='qweqwe')
127 user = user_util.create_user(password='qweqwe')
125 user_id = user.user_id
128 user_id = user.user_id
126 self.log_user(user.username, 'qweqwe')
129 self.log_user(user.username, 'qweqwe')
127
130
128 key_data = self.VALID_KEY
131 key_data = self.VALID_KEY
129
132
130 desc = 'MY SSH KEY'
133 desc = 'MY SSH KEY'
131 response = self.app.post(
134 response = self.app.post(
132 route_path('my_account_ssh_keys_add'),
135 route_path('my_account_ssh_keys_add'),
133 {'description': desc, 'key_data': key_data,
136 {'description': desc, 'key_data': key_data,
134 'csrf_token': self.csrf_token})
137 'csrf_token': self.csrf_token})
135 assert_session_flash(response, 'Ssh Key successfully created')
138 assert_session_flash(response, 'Ssh Key successfully created')
136 response = response.follow() # flush the Session flash
139 response = response.follow() # flush the Session flash
137
140
138 # now delete our key
141 # now delete our key
139 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
142 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
140 assert 1 == len(keys)
143 assert 1 == len(keys)
141
144
142 response = self.app.post(
145 response = self.app.post(
143 route_path('my_account_ssh_keys_delete'),
146 route_path('my_account_ssh_keys_delete'),
144 {'del_ssh_key': keys[0].ssh_key_id,
147 {'del_ssh_key': keys[0].ssh_key_id,
145 'csrf_token': self.csrf_token})
148 'csrf_token': self.csrf_token})
146
149
147 assert_session_flash(response, 'Ssh key successfully deleted')
150 assert_session_flash(response, 'Ssh key successfully deleted')
148 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
151 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
149 assert 0 == len(keys)
152 assert 0 == len(keys)
150
153
151 def test_generate_keypair(self, user_util):
154 def test_generate_keypair(self, user_util):
152 user = user_util.create_user(password='qweqwe')
155 user = user_util.create_user(password='qweqwe')
153 self.log_user(user.username, 'qweqwe')
156 self.log_user(user.username, 'qweqwe')
154
157
155 response = self.app.get(
158 response = self.app.get(
156 route_path('my_account_ssh_keys_generate'))
159 route_path('my_account_ssh_keys_generate'))
157
160
158 response.mustcontain('Private key')
161 response.mustcontain('Private key')
159 response.mustcontain('Public key')
162 response.mustcontain('Public key')
160 response.mustcontain('-----BEGIN RSA PRIVATE KEY-----')
163 response.mustcontain('-----BEGIN RSA PRIVATE KEY-----')
@@ -1,154 +1,155 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView, DataGridAppView
26 from rhodecode.apps._base import BaseAppView, DataGridAppView
27 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
27 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
28 from rhodecode.events import trigger
28 from rhodecode.events import trigger
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib import audit_logger
30 from rhodecode.lib import audit_logger
31 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
31 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
32 from rhodecode.model.db import IntegrityError, UserSshKeys
32 from rhodecode.model.db import IntegrityError, UserSshKeys
33 from rhodecode.model.meta import Session
33 from rhodecode.model.meta import Session
34 from rhodecode.model.ssh_key import SshKeyModel
34 from rhodecode.model.ssh_key import SshKeyModel
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class MyAccountSshKeysView(BaseAppView, DataGridAppView):
39 class MyAccountSshKeysView(BaseAppView, DataGridAppView):
40
40
41 def load_default_context(self):
41 def load_default_context(self):
42 c = self._get_local_tmpl_context()
42 c = self._get_local_tmpl_context()
43 c.user = c.auth_user.get_instance()
43 c.user = c.auth_user.get_instance()
44
44
45 c.ssh_enabled = self.request.registry.settings.get(
45 c.ssh_enabled = self.request.registry.settings.get(
46 'ssh.generate_authorized_keyfile')
46 'ssh.generate_authorized_keyfile')
47
47
48 return c
48 return c
49
49
50 @LoginRequired()
50 @LoginRequired()
51 @NotAnonymous()
51 @NotAnonymous()
52 @view_config(
52 @view_config(
53 route_name='my_account_ssh_keys', request_method='GET',
53 route_name='my_account_ssh_keys', request_method='GET',
54 renderer='rhodecode:templates/admin/my_account/my_account.mako')
54 renderer='rhodecode:templates/admin/my_account/my_account.mako')
55 def my_account_ssh_keys(self):
55 def my_account_ssh_keys(self):
56 _ = self.request.translate
56 _ = self.request.translate
57
57
58 c = self.load_default_context()
58 c = self.load_default_context()
59 c.active = 'ssh_keys'
59 c.active = 'ssh_keys'
60 c.default_key = self.request.GET.get('default_key')
60 c.default_key = self.request.GET.get('default_key')
61 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
61 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
62 return self._get_template_context(c)
62 return self._get_template_context(c)
63
63
64 @LoginRequired()
64 @LoginRequired()
65 @NotAnonymous()
65 @NotAnonymous()
66 @view_config(
66 @view_config(
67 route_name='my_account_ssh_keys_generate', request_method='GET',
67 route_name='my_account_ssh_keys_generate', request_method='GET',
68 renderer='rhodecode:templates/admin/my_account/my_account.mako')
68 renderer='rhodecode:templates/admin/my_account/my_account.mako')
69 def ssh_keys_generate_keypair(self):
69 def ssh_keys_generate_keypair(self):
70 _ = self.request.translate
70 _ = self.request.translate
71 c = self.load_default_context()
71 c = self.load_default_context()
72
72
73 c.active = 'ssh_keys_generate'
73 c.active = 'ssh_keys_generate'
74 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
74 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
75 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
75 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
76 c.target_form_url = h.route_path(
76 c.target_form_url = h.route_path(
77 'my_account_ssh_keys', _query=dict(default_key=c.public))
77 'my_account_ssh_keys', _query=dict(default_key=c.public))
78 return self._get_template_context(c)
78 return self._get_template_context(c)
79
79
80 @LoginRequired()
80 @LoginRequired()
81 @NotAnonymous()
81 @NotAnonymous()
82 @CSRFRequired()
82 @CSRFRequired()
83 @view_config(
83 @view_config(
84 route_name='my_account_ssh_keys_add', request_method='POST',)
84 route_name='my_account_ssh_keys_add', request_method='POST',)
85 def my_account_ssh_keys_add(self):
85 def my_account_ssh_keys_add(self):
86 _ = self.request.translate
86 _ = self.request.translate
87 c = self.load_default_context()
87 c = self.load_default_context()
88
88
89 user_data = c.user.get_api_data()
89 user_data = c.user.get_api_data()
90 key_data = self.request.POST.get('key_data')
90 key_data = self.request.POST.get('key_data')
91 description = self.request.POST.get('description')
91 description = self.request.POST.get('description')
92
92 fingerprint = 'unknown'
93 try:
93 try:
94 if not key_data:
94 if not key_data:
95 raise ValueError('Please add a valid public key')
95 raise ValueError('Please add a valid public key')
96
96
97 key = SshKeyModel().parse_key(key_data.strip())
97 key = SshKeyModel().parse_key(key_data.strip())
98 fingerprint = key.hash_md5()
98 fingerprint = key.hash_md5()
99
99
100 ssh_key = SshKeyModel().create(
100 ssh_key = SshKeyModel().create(
101 c.user.user_id, fingerprint, key_data, description)
101 c.user.user_id, fingerprint, key_data, description)
102 ssh_key_data = ssh_key.get_api_data()
102 ssh_key_data = ssh_key.get_api_data()
103
103
104 audit_logger.store_web(
104 audit_logger.store_web(
105 'user.edit.ssh_key.add', action_data={
105 'user.edit.ssh_key.add', action_data={
106 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
106 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
107 user=self._rhodecode_user, )
107 user=self._rhodecode_user, )
108 Session().commit()
108 Session().commit()
109
109
110 # Trigger an event on change of keys.
110 # Trigger an event on change of keys.
111 trigger(SshKeyFileChangeEvent(), self.request.registry)
111 trigger(SshKeyFileChangeEvent(), self.request.registry)
112
112
113 h.flash(_("Ssh Key successfully created"), category='success')
113 h.flash(_("Ssh Key successfully created"), category='success')
114
114
115 except IntegrityError:
115 except IntegrityError:
116 log.exception("Exception during ssh key saving")
116 log.exception("Exception during ssh key saving")
117 h.flash(_('An error occurred during ssh key saving: {}').format(
117 err = 'Such key with fingerprint `{}` already exists, ' \
118 'Such key already exists, please use a different one'),
118 'please use a different one'.format(fingerprint)
119 h.flash(_('An error occurred during ssh key saving: {}').format(err),
119 category='error')
120 category='error')
120 except Exception as e:
121 except Exception as e:
121 log.exception("Exception during ssh key saving")
122 log.exception("Exception during ssh key saving")
122 h.flash(_('An error occurred during ssh key saving: {}').format(e),
123 h.flash(_('An error occurred during ssh key saving: {}').format(e),
123 category='error')
124 category='error')
124
125
125 return HTTPFound(h.route_path('my_account_ssh_keys'))
126 return HTTPFound(h.route_path('my_account_ssh_keys'))
126
127
127 @LoginRequired()
128 @LoginRequired()
128 @NotAnonymous()
129 @NotAnonymous()
129 @CSRFRequired()
130 @CSRFRequired()
130 @view_config(
131 @view_config(
131 route_name='my_account_ssh_keys_delete', request_method='POST')
132 route_name='my_account_ssh_keys_delete', request_method='POST')
132 def my_account_ssh_keys_delete(self):
133 def my_account_ssh_keys_delete(self):
133 _ = self.request.translate
134 _ = self.request.translate
134 c = self.load_default_context()
135 c = self.load_default_context()
135
136
136 user_data = c.user.get_api_data()
137 user_data = c.user.get_api_data()
137
138
138 del_ssh_key = self.request.POST.get('del_ssh_key')
139 del_ssh_key = self.request.POST.get('del_ssh_key')
139
140
140 if del_ssh_key:
141 if del_ssh_key:
141 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
142 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
142 ssh_key_data = ssh_key.get_api_data()
143 ssh_key_data = ssh_key.get_api_data()
143
144
144 SshKeyModel().delete(del_ssh_key, c.user.user_id)
145 SshKeyModel().delete(del_ssh_key, c.user.user_id)
145 audit_logger.store_web(
146 audit_logger.store_web(
146 'user.edit.ssh_key.delete', action_data={
147 'user.edit.ssh_key.delete', action_data={
147 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
148 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
148 user=self._rhodecode_user,)
149 user=self._rhodecode_user,)
149 Session().commit()
150 Session().commit()
150 # Trigger an event on change of keys.
151 # Trigger an event on change of keys.
151 trigger(SshKeyFileChangeEvent(), self.request.registry)
152 trigger(SshKeyFileChangeEvent(), self.request.registry)
152 h.flash(_("Ssh key successfully deleted"), category='success')
153 h.flash(_("Ssh key successfully deleted"), category='success')
153
154
154 return HTTPFound(h.route_path('my_account_ssh_keys'))
155 return HTTPFound(h.route_path('my_account_ssh_keys'))
General Comments 0
You need to be logged in to leave comments. Login now