##// END OF EJS Templates
chore(imports): optimize imports for ssh events
super-admin -
r5318:ad20d5fb default
parent child Browse files
Show More
@@ -1,477 +1,477 b''
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import re
19 import re
20 import logging
20 import logging
21 import formencode
21 import formencode
22 import formencode.htmlfill
22 import formencode.htmlfill
23 import datetime
23 import datetime
24 from pyramid.interfaces import IRoutesMapper
24 from pyramid.interfaces import IRoutesMapper
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode.apps._base import BaseAppView, DataGridAppView
30 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
31 from rhodecode.apps.ssh_support.events import SshKeyFileChangeEvent
32 from rhodecode import events
32 from rhodecode import events
33
33
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.auth import (
35 from rhodecode.lib.auth import (
36 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
36 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
37 from rhodecode.lib.utils2 import aslist, safe_str
37 from rhodecode.lib.utils2 import aslist, safe_str
38 from rhodecode.model.db import (
38 from rhodecode.model.db import (
39 or_, coalesce, User, UserIpMap, UserSshKeys)
39 or_, coalesce, User, UserIpMap, UserSshKeys)
40 from rhodecode.model.forms import (
40 from rhodecode.model.forms import (
41 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
41 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
42 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
43 from rhodecode.model.permission import PermissionModel
43 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
45
45
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminPermissionsView(BaseAppView, DataGridAppView):
50 class AdminPermissionsView(BaseAppView, DataGridAppView):
51 def load_default_context(self):
51 def load_default_context(self):
52 c = self._get_local_tmpl_context()
52 c = self._get_local_tmpl_context()
53 PermissionModel().set_global_permission_choices(
53 PermissionModel().set_global_permission_choices(
54 c, gettext_translator=self.request.translate)
54 c, gettext_translator=self.request.translate)
55 return c
55 return c
56
56
57 @LoginRequired()
57 @LoginRequired()
58 @HasPermissionAllDecorator('hg.admin')
58 @HasPermissionAllDecorator('hg.admin')
59 def permissions_application(self):
59 def permissions_application(self):
60 c = self.load_default_context()
60 c = self.load_default_context()
61 c.active = 'application'
61 c.active = 'application'
62
62
63 c.user = User.get_default_user(refresh=True)
63 c.user = User.get_default_user(refresh=True)
64
64
65 app_settings = c.rc_config
65 app_settings = c.rc_config
66
66
67 defaults = {
67 defaults = {
68 'anonymous': c.user.active,
68 'anonymous': c.user.active,
69 'default_register_message': app_settings.get(
69 'default_register_message': app_settings.get(
70 'rhodecode_register_message')
70 'rhodecode_register_message')
71 }
71 }
72 defaults.update(c.user.get_default_perms())
72 defaults.update(c.user.get_default_perms())
73
73
74 data = render('rhodecode:templates/admin/permissions/permissions.mako',
74 data = render('rhodecode:templates/admin/permissions/permissions.mako',
75 self._get_template_context(c), self.request)
75 self._get_template_context(c), self.request)
76 html = formencode.htmlfill.render(
76 html = formencode.htmlfill.render(
77 data,
77 data,
78 defaults=defaults,
78 defaults=defaults,
79 encoding="UTF-8",
79 encoding="UTF-8",
80 force_defaults=False
80 force_defaults=False
81 )
81 )
82 return Response(html)
82 return Response(html)
83
83
84 @LoginRequired()
84 @LoginRequired()
85 @HasPermissionAllDecorator('hg.admin')
85 @HasPermissionAllDecorator('hg.admin')
86 @CSRFRequired()
86 @CSRFRequired()
87 def permissions_application_update(self):
87 def permissions_application_update(self):
88 _ = self.request.translate
88 _ = self.request.translate
89 c = self.load_default_context()
89 c = self.load_default_context()
90 c.active = 'application'
90 c.active = 'application'
91
91
92 _form = ApplicationPermissionsForm(
92 _form = ApplicationPermissionsForm(
93 self.request.translate,
93 self.request.translate,
94 [x[0] for x in c.register_choices],
94 [x[0] for x in c.register_choices],
95 [x[0] for x in c.password_reset_choices],
95 [x[0] for x in c.password_reset_choices],
96 [x[0] for x in c.extern_activate_choices])()
96 [x[0] for x in c.extern_activate_choices])()
97
97
98 try:
98 try:
99 form_result = _form.to_python(dict(self.request.POST))
99 form_result = _form.to_python(dict(self.request.POST))
100 form_result.update({'perm_user_name': User.DEFAULT_USER})
100 form_result.update({'perm_user_name': User.DEFAULT_USER})
101 PermissionModel().update_application_permissions(form_result)
101 PermissionModel().update_application_permissions(form_result)
102
102
103 settings = [
103 settings = [
104 ('register_message', 'default_register_message'),
104 ('register_message', 'default_register_message'),
105 ]
105 ]
106 for setting, form_key in settings:
106 for setting, form_key in settings:
107 sett = SettingsModel().create_or_update_setting(
107 sett = SettingsModel().create_or_update_setting(
108 setting, form_result[form_key])
108 setting, form_result[form_key])
109 Session().add(sett)
109 Session().add(sett)
110
110
111 Session().commit()
111 Session().commit()
112 h.flash(_('Application permissions updated successfully'),
112 h.flash(_('Application permissions updated successfully'),
113 category='success')
113 category='success')
114
114
115 except formencode.Invalid as errors:
115 except formencode.Invalid as errors:
116 defaults = errors.value
116 defaults = errors.value
117
117
118 data = render(
118 data = render(
119 'rhodecode:templates/admin/permissions/permissions.mako',
119 'rhodecode:templates/admin/permissions/permissions.mako',
120 self._get_template_context(c), self.request)
120 self._get_template_context(c), self.request)
121 html = formencode.htmlfill.render(
121 html = formencode.htmlfill.render(
122 data,
122 data,
123 defaults=defaults,
123 defaults=defaults,
124 errors=errors.unpack_errors() or {},
124 errors=errors.unpack_errors() or {},
125 prefix_error=False,
125 prefix_error=False,
126 encoding="UTF-8",
126 encoding="UTF-8",
127 force_defaults=False
127 force_defaults=False
128 )
128 )
129 return Response(html)
129 return Response(html)
130
130
131 except Exception:
131 except Exception:
132 log.exception("Exception during update of permissions")
132 log.exception("Exception during update of permissions")
133 h.flash(_('Error occurred during update of permissions'),
133 h.flash(_('Error occurred during update of permissions'),
134 category='error')
134 category='error')
135
135
136 affected_user_ids = [User.get_default_user_id()]
136 affected_user_ids = [User.get_default_user_id()]
137 PermissionModel().trigger_permission_flush(affected_user_ids)
137 PermissionModel().trigger_permission_flush(affected_user_ids)
138
138
139 raise HTTPFound(h.route_path('admin_permissions_application'))
139 raise HTTPFound(h.route_path('admin_permissions_application'))
140
140
141 @LoginRequired()
141 @LoginRequired()
142 @HasPermissionAllDecorator('hg.admin')
142 @HasPermissionAllDecorator('hg.admin')
143 def permissions_objects(self):
143 def permissions_objects(self):
144 c = self.load_default_context()
144 c = self.load_default_context()
145 c.active = 'objects'
145 c.active = 'objects'
146
146
147 c.user = User.get_default_user(refresh=True)
147 c.user = User.get_default_user(refresh=True)
148 defaults = {}
148 defaults = {}
149 defaults.update(c.user.get_default_perms())
149 defaults.update(c.user.get_default_perms())
150
150
151 data = render(
151 data = render(
152 'rhodecode:templates/admin/permissions/permissions.mako',
152 'rhodecode:templates/admin/permissions/permissions.mako',
153 self._get_template_context(c), self.request)
153 self._get_template_context(c), self.request)
154 html = formencode.htmlfill.render(
154 html = formencode.htmlfill.render(
155 data,
155 data,
156 defaults=defaults,
156 defaults=defaults,
157 encoding="UTF-8",
157 encoding="UTF-8",
158 force_defaults=False
158 force_defaults=False
159 )
159 )
160 return Response(html)
160 return Response(html)
161
161
162 @LoginRequired()
162 @LoginRequired()
163 @HasPermissionAllDecorator('hg.admin')
163 @HasPermissionAllDecorator('hg.admin')
164 @CSRFRequired()
164 @CSRFRequired()
165 def permissions_objects_update(self):
165 def permissions_objects_update(self):
166 _ = self.request.translate
166 _ = self.request.translate
167 c = self.load_default_context()
167 c = self.load_default_context()
168 c.active = 'objects'
168 c.active = 'objects'
169
169
170 _form = ObjectPermissionsForm(
170 _form = ObjectPermissionsForm(
171 self.request.translate,
171 self.request.translate,
172 [x[0] for x in c.repo_perms_choices],
172 [x[0] for x in c.repo_perms_choices],
173 [x[0] for x in c.group_perms_choices],
173 [x[0] for x in c.group_perms_choices],
174 [x[0] for x in c.user_group_perms_choices],
174 [x[0] for x in c.user_group_perms_choices],
175 )()
175 )()
176
176
177 try:
177 try:
178 form_result = _form.to_python(dict(self.request.POST))
178 form_result = _form.to_python(dict(self.request.POST))
179 form_result.update({'perm_user_name': User.DEFAULT_USER})
179 form_result.update({'perm_user_name': User.DEFAULT_USER})
180 PermissionModel().update_object_permissions(form_result)
180 PermissionModel().update_object_permissions(form_result)
181
181
182 Session().commit()
182 Session().commit()
183 h.flash(_('Object permissions updated successfully'),
183 h.flash(_('Object permissions updated successfully'),
184 category='success')
184 category='success')
185
185
186 except formencode.Invalid as errors:
186 except formencode.Invalid as errors:
187 defaults = errors.value
187 defaults = errors.value
188
188
189 data = render(
189 data = render(
190 'rhodecode:templates/admin/permissions/permissions.mako',
190 'rhodecode:templates/admin/permissions/permissions.mako',
191 self._get_template_context(c), self.request)
191 self._get_template_context(c), self.request)
192 html = formencode.htmlfill.render(
192 html = formencode.htmlfill.render(
193 data,
193 data,
194 defaults=defaults,
194 defaults=defaults,
195 errors=errors.unpack_errors() or {},
195 errors=errors.unpack_errors() or {},
196 prefix_error=False,
196 prefix_error=False,
197 encoding="UTF-8",
197 encoding="UTF-8",
198 force_defaults=False
198 force_defaults=False
199 )
199 )
200 return Response(html)
200 return Response(html)
201 except Exception:
201 except Exception:
202 log.exception("Exception during update of permissions")
202 log.exception("Exception during update of permissions")
203 h.flash(_('Error occurred during update of permissions'),
203 h.flash(_('Error occurred during update of permissions'),
204 category='error')
204 category='error')
205
205
206 affected_user_ids = [User.get_default_user_id()]
206 affected_user_ids = [User.get_default_user_id()]
207 PermissionModel().trigger_permission_flush(affected_user_ids)
207 PermissionModel().trigger_permission_flush(affected_user_ids)
208
208
209 raise HTTPFound(h.route_path('admin_permissions_object'))
209 raise HTTPFound(h.route_path('admin_permissions_object'))
210
210
211 @LoginRequired()
211 @LoginRequired()
212 @HasPermissionAllDecorator('hg.admin')
212 @HasPermissionAllDecorator('hg.admin')
213 def permissions_branch(self):
213 def permissions_branch(self):
214 c = self.load_default_context()
214 c = self.load_default_context()
215 c.active = 'branch'
215 c.active = 'branch'
216
216
217 c.user = User.get_default_user(refresh=True)
217 c.user = User.get_default_user(refresh=True)
218 defaults = {}
218 defaults = {}
219 defaults.update(c.user.get_default_perms())
219 defaults.update(c.user.get_default_perms())
220
220
221 data = render(
221 data = render(
222 'rhodecode:templates/admin/permissions/permissions.mako',
222 'rhodecode:templates/admin/permissions/permissions.mako',
223 self._get_template_context(c), self.request)
223 self._get_template_context(c), self.request)
224 html = formencode.htmlfill.render(
224 html = formencode.htmlfill.render(
225 data,
225 data,
226 defaults=defaults,
226 defaults=defaults,
227 encoding="UTF-8",
227 encoding="UTF-8",
228 force_defaults=False
228 force_defaults=False
229 )
229 )
230 return Response(html)
230 return Response(html)
231
231
232 @LoginRequired()
232 @LoginRequired()
233 @HasPermissionAllDecorator('hg.admin')
233 @HasPermissionAllDecorator('hg.admin')
234 def permissions_global(self):
234 def permissions_global(self):
235 c = self.load_default_context()
235 c = self.load_default_context()
236 c.active = 'global'
236 c.active = 'global'
237
237
238 c.user = User.get_default_user(refresh=True)
238 c.user = User.get_default_user(refresh=True)
239 defaults = {}
239 defaults = {}
240 defaults.update(c.user.get_default_perms())
240 defaults.update(c.user.get_default_perms())
241
241
242 data = render(
242 data = render(
243 'rhodecode:templates/admin/permissions/permissions.mako',
243 'rhodecode:templates/admin/permissions/permissions.mako',
244 self._get_template_context(c), self.request)
244 self._get_template_context(c), self.request)
245 html = formencode.htmlfill.render(
245 html = formencode.htmlfill.render(
246 data,
246 data,
247 defaults=defaults,
247 defaults=defaults,
248 encoding="UTF-8",
248 encoding="UTF-8",
249 force_defaults=False
249 force_defaults=False
250 )
250 )
251 return Response(html)
251 return Response(html)
252
252
253 @LoginRequired()
253 @LoginRequired()
254 @HasPermissionAllDecorator('hg.admin')
254 @HasPermissionAllDecorator('hg.admin')
255 @CSRFRequired()
255 @CSRFRequired()
256 def permissions_global_update(self):
256 def permissions_global_update(self):
257 _ = self.request.translate
257 _ = self.request.translate
258 c = self.load_default_context()
258 c = self.load_default_context()
259 c.active = 'global'
259 c.active = 'global'
260
260
261 _form = UserPermissionsForm(
261 _form = UserPermissionsForm(
262 self.request.translate,
262 self.request.translate,
263 [x[0] for x in c.repo_create_choices],
263 [x[0] for x in c.repo_create_choices],
264 [x[0] for x in c.repo_create_on_write_choices],
264 [x[0] for x in c.repo_create_on_write_choices],
265 [x[0] for x in c.repo_group_create_choices],
265 [x[0] for x in c.repo_group_create_choices],
266 [x[0] for x in c.user_group_create_choices],
266 [x[0] for x in c.user_group_create_choices],
267 [x[0] for x in c.fork_choices],
267 [x[0] for x in c.fork_choices],
268 [x[0] for x in c.inherit_default_permission_choices])()
268 [x[0] for x in c.inherit_default_permission_choices])()
269
269
270 try:
270 try:
271 form_result = _form.to_python(dict(self.request.POST))
271 form_result = _form.to_python(dict(self.request.POST))
272 form_result.update({'perm_user_name': User.DEFAULT_USER})
272 form_result.update({'perm_user_name': User.DEFAULT_USER})
273 PermissionModel().update_user_permissions(form_result)
273 PermissionModel().update_user_permissions(form_result)
274
274
275 Session().commit()
275 Session().commit()
276 h.flash(_('Global permissions updated successfully'),
276 h.flash(_('Global permissions updated successfully'),
277 category='success')
277 category='success')
278
278
279 except formencode.Invalid as errors:
279 except formencode.Invalid as errors:
280 defaults = errors.value
280 defaults = errors.value
281
281
282 data = render(
282 data = render(
283 'rhodecode:templates/admin/permissions/permissions.mako',
283 'rhodecode:templates/admin/permissions/permissions.mako',
284 self._get_template_context(c), self.request)
284 self._get_template_context(c), self.request)
285 html = formencode.htmlfill.render(
285 html = formencode.htmlfill.render(
286 data,
286 data,
287 defaults=defaults,
287 defaults=defaults,
288 errors=errors.unpack_errors() or {},
288 errors=errors.unpack_errors() or {},
289 prefix_error=False,
289 prefix_error=False,
290 encoding="UTF-8",
290 encoding="UTF-8",
291 force_defaults=False
291 force_defaults=False
292 )
292 )
293 return Response(html)
293 return Response(html)
294 except Exception:
294 except Exception:
295 log.exception("Exception during update of permissions")
295 log.exception("Exception during update of permissions")
296 h.flash(_('Error occurred during update of permissions'),
296 h.flash(_('Error occurred during update of permissions'),
297 category='error')
297 category='error')
298
298
299 affected_user_ids = [User.get_default_user_id()]
299 affected_user_ids = [User.get_default_user_id()]
300 PermissionModel().trigger_permission_flush(affected_user_ids)
300 PermissionModel().trigger_permission_flush(affected_user_ids)
301
301
302 raise HTTPFound(h.route_path('admin_permissions_global'))
302 raise HTTPFound(h.route_path('admin_permissions_global'))
303
303
304 @LoginRequired()
304 @LoginRequired()
305 @HasPermissionAllDecorator('hg.admin')
305 @HasPermissionAllDecorator('hg.admin')
306 def permissions_ips(self):
306 def permissions_ips(self):
307 c = self.load_default_context()
307 c = self.load_default_context()
308 c.active = 'ips'
308 c.active = 'ips'
309
309
310 c.user = User.get_default_user(refresh=True)
310 c.user = User.get_default_user(refresh=True)
311 c.user_ip_map = (
311 c.user_ip_map = (
312 UserIpMap.query().filter(UserIpMap.user == c.user).all())
312 UserIpMap.query().filter(UserIpMap.user == c.user).all())
313
313
314 return self._get_template_context(c)
314 return self._get_template_context(c)
315
315
316 @LoginRequired()
316 @LoginRequired()
317 @HasPermissionAllDecorator('hg.admin')
317 @HasPermissionAllDecorator('hg.admin')
318 def permissions_overview(self):
318 def permissions_overview(self):
319 c = self.load_default_context()
319 c = self.load_default_context()
320 c.active = 'perms'
320 c.active = 'perms'
321
321
322 c.user = User.get_default_user(refresh=True)
322 c.user = User.get_default_user(refresh=True)
323 c.perm_user = c.user.AuthUser()
323 c.perm_user = c.user.AuthUser()
324 return self._get_template_context(c)
324 return self._get_template_context(c)
325
325
326 @LoginRequired()
326 @LoginRequired()
327 @HasPermissionAllDecorator('hg.admin')
327 @HasPermissionAllDecorator('hg.admin')
328 def auth_token_access(self):
328 def auth_token_access(self):
329 from rhodecode import CONFIG
329 from rhodecode import CONFIG
330
330
331 c = self.load_default_context()
331 c = self.load_default_context()
332 c.active = 'auth_token_access'
332 c.active = 'auth_token_access'
333
333
334 c.user = User.get_default_user(refresh=True)
334 c.user = User.get_default_user(refresh=True)
335 c.perm_user = c.user.AuthUser()
335 c.perm_user = c.user.AuthUser()
336
336
337 mapper = self.request.registry.queryUtility(IRoutesMapper)
337 mapper = self.request.registry.queryUtility(IRoutesMapper)
338 c.view_data = []
338 c.view_data = []
339
339
340 _argument_prog = re.compile(r'\{(.*?)\}|:\((.*)\)')
340 _argument_prog = re.compile(r'\{(.*?)\}|:\((.*)\)')
341 introspector = self.request.registry.introspector
341 introspector = self.request.registry.introspector
342
342
343 view_intr = {}
343 view_intr = {}
344 for view_data in introspector.get_category('views'):
344 for view_data in introspector.get_category('views'):
345 intr = view_data['introspectable']
345 intr = view_data['introspectable']
346
346
347 if 'route_name' in intr and intr['attr']:
347 if 'route_name' in intr and intr['attr']:
348 view_intr[intr['route_name']] = '{}:{}'.format(
348 view_intr[intr['route_name']] = '{}:{}'.format(
349 str(intr['derived_callable'].__name__), intr['attr']
349 str(intr['derived_callable'].__name__), intr['attr']
350 )
350 )
351
351
352 c.whitelist_key = 'api_access_controllers_whitelist'
352 c.whitelist_key = 'api_access_controllers_whitelist'
353 c.whitelist_file = CONFIG.get('__file__')
353 c.whitelist_file = CONFIG.get('__file__')
354 whitelist_views = aslist(
354 whitelist_views = aslist(
355 CONFIG.get(c.whitelist_key), sep=',')
355 CONFIG.get(c.whitelist_key), sep=',')
356
356
357 for route_info in mapper.get_routes():
357 for route_info in mapper.get_routes():
358 if not route_info.name.startswith('__'):
358 if not route_info.name.startswith('__'):
359 routepath = route_info.pattern
359 routepath = route_info.pattern
360
360
361 def replace(matchobj):
361 def replace(matchobj):
362 if matchobj.group(1):
362 if matchobj.group(1):
363 return "{%s}" % matchobj.group(1).split(':')[0]
363 return "{%s}" % matchobj.group(1).split(':')[0]
364 else:
364 else:
365 return "{%s}" % matchobj.group(2)
365 return "{%s}" % matchobj.group(2)
366
366
367 routepath = _argument_prog.sub(replace, routepath)
367 routepath = _argument_prog.sub(replace, routepath)
368
368
369 if not routepath.startswith('/'):
369 if not routepath.startswith('/'):
370 routepath = '/' + routepath
370 routepath = '/' + routepath
371
371
372 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
372 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
373 active = view_fqn in whitelist_views
373 active = view_fqn in whitelist_views
374 c.view_data.append((route_info.name, view_fqn, routepath, active))
374 c.view_data.append((route_info.name, view_fqn, routepath, active))
375
375
376 c.whitelist_views = whitelist_views
376 c.whitelist_views = whitelist_views
377 return self._get_template_context(c)
377 return self._get_template_context(c)
378
378
379 def ssh_enabled(self):
379 def ssh_enabled(self):
380 return self.request.registry.settings.get(
380 return self.request.registry.settings.get(
381 'ssh.generate_authorized_keyfile')
381 'ssh.generate_authorized_keyfile')
382
382
383 @LoginRequired()
383 @LoginRequired()
384 @HasPermissionAllDecorator('hg.admin')
384 @HasPermissionAllDecorator('hg.admin')
385 def ssh_keys(self):
385 def ssh_keys(self):
386 c = self.load_default_context()
386 c = self.load_default_context()
387 c.active = 'ssh_keys'
387 c.active = 'ssh_keys'
388 c.ssh_enabled = self.ssh_enabled()
388 c.ssh_enabled = self.ssh_enabled()
389 return self._get_template_context(c)
389 return self._get_template_context(c)
390
390
391 @LoginRequired()
391 @LoginRequired()
392 @HasPermissionAllDecorator('hg.admin')
392 @HasPermissionAllDecorator('hg.admin')
393 def ssh_keys_data(self):
393 def ssh_keys_data(self):
394 _ = self.request.translate
394 _ = self.request.translate
395 self.load_default_context()
395 self.load_default_context()
396 column_map = {
396 column_map = {
397 'fingerprint': 'ssh_key_fingerprint',
397 'fingerprint': 'ssh_key_fingerprint',
398 'username': User.username
398 'username': User.username
399 }
399 }
400 draw, start, limit = self._extract_chunk(self.request)
400 draw, start, limit = self._extract_chunk(self.request)
401 search_q, order_by, order_dir = self._extract_ordering(
401 search_q, order_by, order_dir = self._extract_ordering(
402 self.request, column_map=column_map)
402 self.request, column_map=column_map)
403
403
404 ssh_keys_data_total_count = UserSshKeys.query()\
404 ssh_keys_data_total_count = UserSshKeys.query()\
405 .count()
405 .count()
406
406
407 # json generate
407 # json generate
408 base_q = UserSshKeys.query().join(UserSshKeys.user)
408 base_q = UserSshKeys.query().join(UserSshKeys.user)
409
409
410 if search_q:
410 if search_q:
411 like_expression = f'%{safe_str(search_q)}%'
411 like_expression = f'%{safe_str(search_q)}%'
412 base_q = base_q.filter(or_(
412 base_q = base_q.filter(or_(
413 User.username.ilike(like_expression),
413 User.username.ilike(like_expression),
414 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
414 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
415 ))
415 ))
416
416
417 users_data_total_filtered_count = base_q.count()
417 users_data_total_filtered_count = base_q.count()
418
418
419 sort_col = self._get_order_col(order_by, UserSshKeys)
419 sort_col = self._get_order_col(order_by, UserSshKeys)
420 if sort_col:
420 if sort_col:
421 if order_dir == 'asc':
421 if order_dir == 'asc':
422 # handle null values properly to order by NULL last
422 # handle null values properly to order by NULL last
423 if order_by in ['created_on']:
423 if order_by in ['created_on']:
424 sort_col = coalesce(sort_col, datetime.date.max)
424 sort_col = coalesce(sort_col, datetime.date.max)
425 sort_col = sort_col.asc()
425 sort_col = sort_col.asc()
426 else:
426 else:
427 # handle null values properly to order by NULL last
427 # handle null values properly to order by NULL last
428 if order_by in ['created_on']:
428 if order_by in ['created_on']:
429 sort_col = coalesce(sort_col, datetime.date.min)
429 sort_col = coalesce(sort_col, datetime.date.min)
430 sort_col = sort_col.desc()
430 sort_col = sort_col.desc()
431
431
432 base_q = base_q.order_by(sort_col)
432 base_q = base_q.order_by(sort_col)
433 base_q = base_q.offset(start).limit(limit)
433 base_q = base_q.offset(start).limit(limit)
434
434
435 ssh_keys = base_q.all()
435 ssh_keys = base_q.all()
436
436
437 ssh_keys_data = []
437 ssh_keys_data = []
438 for ssh_key in ssh_keys:
438 for ssh_key in ssh_keys:
439 ssh_keys_data.append({
439 ssh_keys_data.append({
440 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
440 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
441 "fingerprint": ssh_key.ssh_key_fingerprint,
441 "fingerprint": ssh_key.ssh_key_fingerprint,
442 "description": ssh_key.description,
442 "description": ssh_key.description,
443 "created_on": h.format_date(ssh_key.created_on),
443 "created_on": h.format_date(ssh_key.created_on),
444 "accessed_on": h.format_date(ssh_key.accessed_on),
444 "accessed_on": h.format_date(ssh_key.accessed_on),
445 "action": h.link_to(
445 "action": h.link_to(
446 _('Edit'), h.route_path('edit_user_ssh_keys',
446 _('Edit'), h.route_path('edit_user_ssh_keys',
447 user_id=ssh_key.user.user_id))
447 user_id=ssh_key.user.user_id))
448 })
448 })
449
449
450 data = ({
450 data = ({
451 'draw': draw,
451 'draw': draw,
452 'data': ssh_keys_data,
452 'data': ssh_keys_data,
453 'recordsTotal': ssh_keys_data_total_count,
453 'recordsTotal': ssh_keys_data_total_count,
454 'recordsFiltered': users_data_total_filtered_count,
454 'recordsFiltered': users_data_total_filtered_count,
455 })
455 })
456
456
457 return data
457 return data
458
458
459 @LoginRequired()
459 @LoginRequired()
460 @HasPermissionAllDecorator('hg.admin')
460 @HasPermissionAllDecorator('hg.admin')
461 @CSRFRequired()
461 @CSRFRequired()
462 def ssh_keys_update(self):
462 def ssh_keys_update(self):
463 _ = self.request.translate
463 _ = self.request.translate
464 self.load_default_context()
464 self.load_default_context()
465
465
466 ssh_enabled = self.ssh_enabled()
466 ssh_enabled = self.ssh_enabled()
467 key_file = self.request.registry.settings.get(
467 key_file = self.request.registry.settings.get(
468 'ssh.authorized_keys_file_path')
468 'ssh.authorized_keys_file_path')
469 if ssh_enabled:
469 if ssh_enabled:
470 events.trigger(SshKeyFileChangeEvent(), self.request.registry)
470 events.trigger(SshKeyFileChangeEvent(), self.request.registry)
471 h.flash(_('Updated SSH keys file: {}').format(key_file),
471 h.flash(_('Updated SSH keys file: {}').format(key_file),
472 category='success')
472 category='success')
473 else:
473 else:
474 h.flash(_('SSH key support is disabled in .ini file'),
474 h.flash(_('SSH key support is disabled in .ini file'),
475 category='warning')
475 category='warning')
476
476
477 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
477 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -1,1321 +1,1321 b''
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import logging
19 import logging
20 import datetime
20 import datetime
21 import formencode
21 import formencode
22 import formencode.htmlfill
22 import formencode.htmlfill
23
23
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.renderers import render
25 from pyramid.renderers import render
26 from pyramid.response import Response
26 from pyramid.response import Response
27
27
28 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
29 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
30 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
30 from rhodecode.apps.ssh_support.events import SshKeyFileChangeEvent
31 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
31 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
32 from rhodecode.authentication.plugins import auth_rhodecode
32 from rhodecode.authentication.plugins import auth_rhodecode
33 from rhodecode.events import trigger
33 from rhodecode.events import trigger
34 from rhodecode.model.db import true, UserNotice
34 from rhodecode.model.db import true, UserNotice
35
35
36 from rhodecode.lib import audit_logger, rc_cache, auth
36 from rhodecode.lib import audit_logger, rc_cache, auth
37 from rhodecode.lib.exceptions import (
37 from rhodecode.lib.exceptions import (
38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
39 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
39 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
40 UserOwnsArtifactsException, DefaultUserException)
40 UserOwnsArtifactsException, DefaultUserException)
41 from rhodecode.lib import ext_json
41 from rhodecode.lib import ext_json
42 from rhodecode.lib.auth import (
42 from rhodecode.lib.auth import (
43 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
43 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
44 from rhodecode.lib import helpers as h
44 from rhodecode.lib import helpers as h
45 from rhodecode.lib.helpers import SqlPage
45 from rhodecode.lib.helpers import SqlPage
46 from rhodecode.lib.utils2 import safe_int, safe_str, AttributeDict
46 from rhodecode.lib.utils2 import safe_int, safe_str, AttributeDict
47 from rhodecode.model.auth_token import AuthTokenModel
47 from rhodecode.model.auth_token import AuthTokenModel
48 from rhodecode.model.forms import (
48 from rhodecode.model.forms import (
49 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
49 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
50 UserExtraEmailForm, UserExtraIpForm)
50 UserExtraEmailForm, UserExtraIpForm)
51 from rhodecode.model.permission import PermissionModel
51 from rhodecode.model.permission import PermissionModel
52 from rhodecode.model.repo_group import RepoGroupModel
52 from rhodecode.model.repo_group import RepoGroupModel
53 from rhodecode.model.ssh_key import SshKeyModel
53 from rhodecode.model.ssh_key import SshKeyModel
54 from rhodecode.model.user import UserModel
54 from rhodecode.model.user import UserModel
55 from rhodecode.model.user_group import UserGroupModel
55 from rhodecode.model.user_group import UserGroupModel
56 from rhodecode.model.db import (
56 from rhodecode.model.db import (
57 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
57 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
58 UserApiKeys, UserSshKeys, RepoGroup)
58 UserApiKeys, UserSshKeys, RepoGroup)
59 from rhodecode.model.meta import Session
59 from rhodecode.model.meta import Session
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63
63
64 class AdminUsersView(BaseAppView, DataGridAppView):
64 class AdminUsersView(BaseAppView, DataGridAppView):
65
65
66 def load_default_context(self):
66 def load_default_context(self):
67 c = self._get_local_tmpl_context()
67 c = self._get_local_tmpl_context()
68 return c
68 return c
69
69
70 @LoginRequired()
70 @LoginRequired()
71 @HasPermissionAllDecorator('hg.admin')
71 @HasPermissionAllDecorator('hg.admin')
72 def users_list(self):
72 def users_list(self):
73 c = self.load_default_context()
73 c = self.load_default_context()
74 return self._get_template_context(c)
74 return self._get_template_context(c)
75
75
76 @LoginRequired()
76 @LoginRequired()
77 @HasPermissionAllDecorator('hg.admin')
77 @HasPermissionAllDecorator('hg.admin')
78 def users_list_data(self):
78 def users_list_data(self):
79 self.load_default_context()
79 self.load_default_context()
80 column_map = {
80 column_map = {
81 'first_name': 'name',
81 'first_name': 'name',
82 'last_name': 'lastname',
82 'last_name': 'lastname',
83 }
83 }
84 draw, start, limit = self._extract_chunk(self.request)
84 draw, start, limit = self._extract_chunk(self.request)
85 search_q, order_by, order_dir = self._extract_ordering(
85 search_q, order_by, order_dir = self._extract_ordering(
86 self.request, column_map=column_map)
86 self.request, column_map=column_map)
87 _render = self.request.get_partial_renderer(
87 _render = self.request.get_partial_renderer(
88 'rhodecode:templates/data_table/_dt_elements.mako')
88 'rhodecode:templates/data_table/_dt_elements.mako')
89
89
90 def user_actions(user_id, username):
90 def user_actions(user_id, username):
91 return _render("user_actions", user_id, username)
91 return _render("user_actions", user_id, username)
92
92
93 users_data_total_count = User.query()\
93 users_data_total_count = User.query()\
94 .filter(User.username != User.DEFAULT_USER) \
94 .filter(User.username != User.DEFAULT_USER) \
95 .count()
95 .count()
96
96
97 users_data_total_inactive_count = User.query()\
97 users_data_total_inactive_count = User.query()\
98 .filter(User.username != User.DEFAULT_USER) \
98 .filter(User.username != User.DEFAULT_USER) \
99 .filter(User.active != true())\
99 .filter(User.active != true())\
100 .count()
100 .count()
101
101
102 # json generate
102 # json generate
103 base_q = User.query().filter(User.username != User.DEFAULT_USER)
103 base_q = User.query().filter(User.username != User.DEFAULT_USER)
104 base_inactive_q = base_q.filter(User.active != true())
104 base_inactive_q = base_q.filter(User.active != true())
105
105
106 if search_q:
106 if search_q:
107 like_expression = '%{}%'.format(safe_str(search_q))
107 like_expression = '%{}%'.format(safe_str(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 base_inactive_q = base_q.filter(User.active != true())
114 base_inactive_q = base_q.filter(User.active != true())
115
115
116 users_data_total_filtered_count = base_q.count()
116 users_data_total_filtered_count = base_q.count()
117 users_data_total_filtered_inactive_count = base_inactive_q.count()
117 users_data_total_filtered_inactive_count = base_inactive_q.count()
118
118
119 sort_col = getattr(User, order_by, None)
119 sort_col = getattr(User, order_by, None)
120 if sort_col:
120 if sort_col:
121 if order_dir == 'asc':
121 if order_dir == 'asc':
122 # handle null values properly to order by NULL last
122 # handle null values properly to order by NULL last
123 if order_by in ['last_activity']:
123 if order_by in ['last_activity']:
124 sort_col = coalesce(sort_col, datetime.date.max)
124 sort_col = coalesce(sort_col, datetime.date.max)
125 sort_col = sort_col.asc()
125 sort_col = sort_col.asc()
126 else:
126 else:
127 # handle null values properly to order by NULL last
127 # handle null values properly to order by NULL last
128 if order_by in ['last_activity']:
128 if order_by in ['last_activity']:
129 sort_col = coalesce(sort_col, datetime.date.min)
129 sort_col = coalesce(sort_col, datetime.date.min)
130 sort_col = sort_col.desc()
130 sort_col = sort_col.desc()
131
131
132 base_q = base_q.order_by(sort_col)
132 base_q = base_q.order_by(sort_col)
133 base_q = base_q.offset(start).limit(limit)
133 base_q = base_q.offset(start).limit(limit)
134
134
135 users_list = base_q.all()
135 users_list = base_q.all()
136
136
137 users_data = []
137 users_data = []
138 for user in users_list:
138 for user in users_list:
139 users_data.append({
139 users_data.append({
140 "username": h.gravatar_with_user(self.request, user.username),
140 "username": h.gravatar_with_user(self.request, user.username),
141 "email": user.email,
141 "email": user.email,
142 "first_name": user.first_name,
142 "first_name": user.first_name,
143 "last_name": user.last_name,
143 "last_name": user.last_name,
144 "last_login": h.format_date(user.last_login),
144 "last_login": h.format_date(user.last_login),
145 "last_activity": h.format_date(user.last_activity),
145 "last_activity": h.format_date(user.last_activity),
146 "active": h.bool2icon(user.active),
146 "active": h.bool2icon(user.active),
147 "active_raw": user.active,
147 "active_raw": user.active,
148 "admin": h.bool2icon(user.admin),
148 "admin": h.bool2icon(user.admin),
149 "extern_type": user.extern_type,
149 "extern_type": user.extern_type,
150 "extern_name": user.extern_name,
150 "extern_name": user.extern_name,
151 "action": user_actions(user.user_id, user.username),
151 "action": user_actions(user.user_id, user.username),
152 })
152 })
153 data = ({
153 data = ({
154 'draw': draw,
154 'draw': draw,
155 'data': users_data,
155 'data': users_data,
156 'recordsTotal': users_data_total_count,
156 'recordsTotal': users_data_total_count,
157 'recordsFiltered': users_data_total_filtered_count,
157 'recordsFiltered': users_data_total_filtered_count,
158 'recordsTotalInactive': users_data_total_inactive_count,
158 'recordsTotalInactive': users_data_total_inactive_count,
159 'recordsFilteredInactive': users_data_total_filtered_inactive_count
159 'recordsFilteredInactive': users_data_total_filtered_inactive_count
160 })
160 })
161
161
162 return data
162 return data
163
163
164 def _set_personal_repo_group_template_vars(self, c_obj):
164 def _set_personal_repo_group_template_vars(self, c_obj):
165 DummyUser = AttributeDict({
165 DummyUser = AttributeDict({
166 'username': '${username}',
166 'username': '${username}',
167 'user_id': '${user_id}',
167 'user_id': '${user_id}',
168 })
168 })
169 c_obj.default_create_repo_group = RepoGroupModel() \
169 c_obj.default_create_repo_group = RepoGroupModel() \
170 .get_default_create_personal_repo_group()
170 .get_default_create_personal_repo_group()
171 c_obj.personal_repo_group_name = RepoGroupModel() \
171 c_obj.personal_repo_group_name = RepoGroupModel() \
172 .get_personal_group_name(DummyUser)
172 .get_personal_group_name(DummyUser)
173
173
174 @LoginRequired()
174 @LoginRequired()
175 @HasPermissionAllDecorator('hg.admin')
175 @HasPermissionAllDecorator('hg.admin')
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.uid
179 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
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 def users_create(self):
186 def users_create(self):
187 _ = self.request.translate
187 _ = self.request.translate
188 c = self.load_default_context()
188 c = self.load_default_context()
189 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
189 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
190 user_model = UserModel()
190 user_model = UserModel()
191 user_form = UserForm(self.request.translate)()
191 user_form = UserForm(self.request.translate)()
192 try:
192 try:
193 form_result = user_form.to_python(dict(self.request.POST))
193 form_result = user_form.to_python(dict(self.request.POST))
194 user = user_model.create(form_result)
194 user = user_model.create(form_result)
195 Session().flush()
195 Session().flush()
196 creation_data = user.get_api_data()
196 creation_data = user.get_api_data()
197 username = form_result['username']
197 username = form_result['username']
198
198
199 audit_logger.store_web(
199 audit_logger.store_web(
200 'user.create', action_data={'data': creation_data},
200 'user.create', action_data={'data': creation_data},
201 user=c.rhodecode_user)
201 user=c.rhodecode_user)
202
202
203 user_link = h.link_to(
203 user_link = h.link_to(
204 h.escape(username),
204 h.escape(username),
205 h.route_path('user_edit', user_id=user.user_id))
205 h.route_path('user_edit', user_id=user.user_id))
206 h.flash(h.literal(_('Created user %(user_link)s')
206 h.flash(h.literal(_('Created user %(user_link)s')
207 % {'user_link': user_link}), category='success')
207 % {'user_link': user_link}), category='success')
208 Session().commit()
208 Session().commit()
209 except formencode.Invalid as errors:
209 except formencode.Invalid as errors:
210 self._set_personal_repo_group_template_vars(c)
210 self._set_personal_repo_group_template_vars(c)
211 data = render(
211 data = render(
212 'rhodecode:templates/admin/users/user_add.mako',
212 'rhodecode:templates/admin/users/user_add.mako',
213 self._get_template_context(c), self.request)
213 self._get_template_context(c), self.request)
214 html = formencode.htmlfill.render(
214 html = formencode.htmlfill.render(
215 data,
215 data,
216 defaults=errors.value,
216 defaults=errors.value,
217 errors=errors.unpack_errors() or {},
217 errors=errors.unpack_errors() or {},
218 prefix_error=False,
218 prefix_error=False,
219 encoding="UTF-8",
219 encoding="UTF-8",
220 force_defaults=False
220 force_defaults=False
221 )
221 )
222 return Response(html)
222 return Response(html)
223 except UserCreationError as e:
223 except UserCreationError as e:
224 h.flash(safe_str(e), 'error')
224 h.flash(safe_str(e), 'error')
225 except Exception:
225 except Exception:
226 log.exception("Exception creation of user")
226 log.exception("Exception creation of user")
227 h.flash(_('Error occurred during creation of user %s')
227 h.flash(_('Error occurred during creation of user %s')
228 % self.request.POST.get('username'), category='error')
228 % self.request.POST.get('username'), category='error')
229 raise HTTPFound(h.route_path('users'))
229 raise HTTPFound(h.route_path('users'))
230
230
231
231
232 class UsersView(UserAppView):
232 class UsersView(UserAppView):
233 ALLOW_SCOPED_TOKENS = False
233 ALLOW_SCOPED_TOKENS = False
234 """
234 """
235 This view has alternative version inside EE, if modified please take a look
235 This view has alternative version inside EE, if modified please take a look
236 in there as well.
236 in there as well.
237 """
237 """
238
238
239 def get_auth_plugins(self):
239 def get_auth_plugins(self):
240 valid_plugins = []
240 valid_plugins = []
241 authn_registry = get_authn_registry(self.request.registry)
241 authn_registry = get_authn_registry(self.request.registry)
242 for plugin in authn_registry.get_plugins_for_authentication():
242 for plugin in authn_registry.get_plugins_for_authentication():
243 if isinstance(plugin, RhodeCodeExternalAuthPlugin):
243 if isinstance(plugin, RhodeCodeExternalAuthPlugin):
244 valid_plugins.append(plugin)
244 valid_plugins.append(plugin)
245 elif plugin.name == 'rhodecode':
245 elif plugin.name == 'rhodecode':
246 valid_plugins.append(plugin)
246 valid_plugins.append(plugin)
247
247
248 # extend our choices if user has set a bound plugin which isn't enabled at the
248 # extend our choices if user has set a bound plugin which isn't enabled at the
249 # moment
249 # moment
250 extern_type = self.db_user.extern_type
250 extern_type = self.db_user.extern_type
251 if extern_type not in [x.uid for x in valid_plugins]:
251 if extern_type not in [x.uid for x in valid_plugins]:
252 try:
252 try:
253 plugin = authn_registry.get_plugin_by_uid(extern_type)
253 plugin = authn_registry.get_plugin_by_uid(extern_type)
254 if plugin:
254 if plugin:
255 valid_plugins.append(plugin)
255 valid_plugins.append(plugin)
256
256
257 except Exception:
257 except Exception:
258 log.exception(
258 log.exception(
259 f'Could not extend user plugins with `{extern_type}`')
259 f'Could not extend user plugins with `{extern_type}`')
260 return valid_plugins
260 return valid_plugins
261
261
262 def load_default_context(self):
262 def load_default_context(self):
263 req = self.request
263 req = self.request
264
264
265 c = self._get_local_tmpl_context()
265 c = self._get_local_tmpl_context()
266 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
266 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
267 c.allowed_languages = [
267 c.allowed_languages = [
268 ('en', 'English (en)'),
268 ('en', 'English (en)'),
269 ('de', 'German (de)'),
269 ('de', 'German (de)'),
270 ('fr', 'French (fr)'),
270 ('fr', 'French (fr)'),
271 ('it', 'Italian (it)'),
271 ('it', 'Italian (it)'),
272 ('ja', 'Japanese (ja)'),
272 ('ja', 'Japanese (ja)'),
273 ('pl', 'Polish (pl)'),
273 ('pl', 'Polish (pl)'),
274 ('pt', 'Portuguese (pt)'),
274 ('pt', 'Portuguese (pt)'),
275 ('ru', 'Russian (ru)'),
275 ('ru', 'Russian (ru)'),
276 ('zh', 'Chinese (zh)'),
276 ('zh', 'Chinese (zh)'),
277 ]
277 ]
278
278
279 c.allowed_extern_types = [
279 c.allowed_extern_types = [
280 (x.uid, x.get_display_name()) for x in self.get_auth_plugins()
280 (x.uid, x.get_display_name()) for x in self.get_auth_plugins()
281 ]
281 ]
282 perms = req.registry.settings.get('available_permissions')
282 perms = req.registry.settings.get('available_permissions')
283 if not perms:
283 if not perms:
284 # inject info about available permissions
284 # inject info about available permissions
285 auth.set_available_permissions(req.registry.settings)
285 auth.set_available_permissions(req.registry.settings)
286
286
287 c.available_permissions = req.registry.settings['available_permissions']
287 c.available_permissions = req.registry.settings['available_permissions']
288 PermissionModel().set_global_permission_choices(
288 PermissionModel().set_global_permission_choices(
289 c, gettext_translator=req.translate)
289 c, gettext_translator=req.translate)
290
290
291 return c
291 return c
292
292
293 @LoginRequired()
293 @LoginRequired()
294 @HasPermissionAllDecorator('hg.admin')
294 @HasPermissionAllDecorator('hg.admin')
295 @CSRFRequired()
295 @CSRFRequired()
296 def user_update(self):
296 def user_update(self):
297 _ = self.request.translate
297 _ = self.request.translate
298 c = self.load_default_context()
298 c = self.load_default_context()
299
299
300 user_id = self.db_user_id
300 user_id = self.db_user_id
301 c.user = self.db_user
301 c.user = self.db_user
302
302
303 c.active = 'profile'
303 c.active = 'profile'
304 c.extern_type = c.user.extern_type
304 c.extern_type = c.user.extern_type
305 c.extern_name = c.user.extern_name
305 c.extern_name = c.user.extern_name
306 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
306 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
307 available_languages = [x[0] for x in c.allowed_languages]
307 available_languages = [x[0] for x in c.allowed_languages]
308 _form = UserForm(self.request.translate, edit=True,
308 _form = UserForm(self.request.translate, edit=True,
309 available_languages=available_languages,
309 available_languages=available_languages,
310 old_data={'user_id': user_id,
310 old_data={'user_id': user_id,
311 'email': c.user.email})()
311 'email': c.user.email})()
312
312
313 c.edit_mode = self.request.POST.get('edit') == '1'
313 c.edit_mode = self.request.POST.get('edit') == '1'
314 form_result = {}
314 form_result = {}
315 old_values = c.user.get_api_data()
315 old_values = c.user.get_api_data()
316 try:
316 try:
317 form_result = _form.to_python(dict(self.request.POST))
317 form_result = _form.to_python(dict(self.request.POST))
318 skip_attrs = ['extern_name']
318 skip_attrs = ['extern_name']
319 # TODO: plugin should define if username can be updated
319 # TODO: plugin should define if username can be updated
320
320
321 if c.extern_type != "rhodecode" and not c.edit_mode:
321 if c.extern_type != "rhodecode" and not c.edit_mode:
322 # forbid updating username for external accounts
322 # forbid updating username for external accounts
323 skip_attrs.append('username')
323 skip_attrs.append('username')
324
324
325 UserModel().update_user(
325 UserModel().update_user(
326 user_id, skip_attrs=skip_attrs, **form_result)
326 user_id, skip_attrs=skip_attrs, **form_result)
327
327
328 audit_logger.store_web(
328 audit_logger.store_web(
329 'user.edit', action_data={'old_data': old_values},
329 'user.edit', action_data={'old_data': old_values},
330 user=c.rhodecode_user)
330 user=c.rhodecode_user)
331
331
332 Session().commit()
332 Session().commit()
333 h.flash(_('User updated successfully'), category='success')
333 h.flash(_('User updated successfully'), category='success')
334 except formencode.Invalid as errors:
334 except formencode.Invalid as errors:
335 data = render(
335 data = render(
336 'rhodecode:templates/admin/users/user_edit.mako',
336 'rhodecode:templates/admin/users/user_edit.mako',
337 self._get_template_context(c), self.request)
337 self._get_template_context(c), self.request)
338 html = formencode.htmlfill.render(
338 html = formencode.htmlfill.render(
339 data,
339 data,
340 defaults=errors.value,
340 defaults=errors.value,
341 errors=errors.unpack_errors() or {},
341 errors=errors.unpack_errors() or {},
342 prefix_error=False,
342 prefix_error=False,
343 encoding="UTF-8",
343 encoding="UTF-8",
344 force_defaults=False
344 force_defaults=False
345 )
345 )
346 return Response(html)
346 return Response(html)
347 except UserCreationError as e:
347 except UserCreationError as e:
348 h.flash(safe_str(e), 'error')
348 h.flash(safe_str(e), 'error')
349 except Exception:
349 except Exception:
350 log.exception("Exception updating user")
350 log.exception("Exception updating user")
351 h.flash(_('Error occurred during update of user %s')
351 h.flash(_('Error occurred during update of user %s')
352 % form_result.get('username'), category='error')
352 % form_result.get('username'), category='error')
353 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
353 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
354
354
355 @LoginRequired()
355 @LoginRequired()
356 @HasPermissionAllDecorator('hg.admin')
356 @HasPermissionAllDecorator('hg.admin')
357 @CSRFRequired()
357 @CSRFRequired()
358 def user_delete(self):
358 def user_delete(self):
359 _ = self.request.translate
359 _ = self.request.translate
360 c = self.load_default_context()
360 c = self.load_default_context()
361 c.user = self.db_user
361 c.user = self.db_user
362
362
363 _repos = len(c.user.repositories)
363 _repos = len(c.user.repositories)
364 _repo_groups = len(c.user.repository_groups)
364 _repo_groups = len(c.user.repository_groups)
365 _user_groups = len(c.user.user_groups)
365 _user_groups = len(c.user.user_groups)
366 _pull_requests = len(c.user.user_pull_requests)
366 _pull_requests = len(c.user.user_pull_requests)
367 _artifacts = len(c.user.artifacts)
367 _artifacts = len(c.user.artifacts)
368
368
369 handle_repos = None
369 handle_repos = None
370 handle_repo_groups = None
370 handle_repo_groups = None
371 handle_user_groups = None
371 handle_user_groups = None
372 handle_pull_requests = None
372 handle_pull_requests = None
373 handle_artifacts = None
373 handle_artifacts = None
374
374
375 # calls for flash of handle based on handle case detach or delete
375 # calls for flash of handle based on handle case detach or delete
376 def set_handle_flash_repos():
376 def set_handle_flash_repos():
377 handle = handle_repos
377 handle = handle_repos
378 if handle == 'detach':
378 if handle == 'detach':
379 h.flash(_('Detached %s repositories') % _repos,
379 h.flash(_('Detached %s repositories') % _repos,
380 category='success')
380 category='success')
381 elif handle == 'delete':
381 elif handle == 'delete':
382 h.flash(_('Deleted %s repositories') % _repos,
382 h.flash(_('Deleted %s repositories') % _repos,
383 category='success')
383 category='success')
384
384
385 def set_handle_flash_repo_groups():
385 def set_handle_flash_repo_groups():
386 handle = handle_repo_groups
386 handle = handle_repo_groups
387 if handle == 'detach':
387 if handle == 'detach':
388 h.flash(_('Detached %s repository groups') % _repo_groups,
388 h.flash(_('Detached %s repository groups') % _repo_groups,
389 category='success')
389 category='success')
390 elif handle == 'delete':
390 elif handle == 'delete':
391 h.flash(_('Deleted %s repository groups') % _repo_groups,
391 h.flash(_('Deleted %s repository groups') % _repo_groups,
392 category='success')
392 category='success')
393
393
394 def set_handle_flash_user_groups():
394 def set_handle_flash_user_groups():
395 handle = handle_user_groups
395 handle = handle_user_groups
396 if handle == 'detach':
396 if handle == 'detach':
397 h.flash(_('Detached %s user groups') % _user_groups,
397 h.flash(_('Detached %s user groups') % _user_groups,
398 category='success')
398 category='success')
399 elif handle == 'delete':
399 elif handle == 'delete':
400 h.flash(_('Deleted %s user groups') % _user_groups,
400 h.flash(_('Deleted %s user groups') % _user_groups,
401 category='success')
401 category='success')
402
402
403 def set_handle_flash_pull_requests():
403 def set_handle_flash_pull_requests():
404 handle = handle_pull_requests
404 handle = handle_pull_requests
405 if handle == 'detach':
405 if handle == 'detach':
406 h.flash(_('Detached %s pull requests') % _pull_requests,
406 h.flash(_('Detached %s pull requests') % _pull_requests,
407 category='success')
407 category='success')
408 elif handle == 'delete':
408 elif handle == 'delete':
409 h.flash(_('Deleted %s pull requests') % _pull_requests,
409 h.flash(_('Deleted %s pull requests') % _pull_requests,
410 category='success')
410 category='success')
411
411
412 def set_handle_flash_artifacts():
412 def set_handle_flash_artifacts():
413 handle = handle_artifacts
413 handle = handle_artifacts
414 if handle == 'detach':
414 if handle == 'detach':
415 h.flash(_('Detached %s artifacts') % _artifacts,
415 h.flash(_('Detached %s artifacts') % _artifacts,
416 category='success')
416 category='success')
417 elif handle == 'delete':
417 elif handle == 'delete':
418 h.flash(_('Deleted %s artifacts') % _artifacts,
418 h.flash(_('Deleted %s artifacts') % _artifacts,
419 category='success')
419 category='success')
420
420
421 handle_user = User.get_first_super_admin()
421 handle_user = User.get_first_super_admin()
422 handle_user_id = safe_int(self.request.POST.get('detach_user_id'))
422 handle_user_id = safe_int(self.request.POST.get('detach_user_id'))
423 if handle_user_id:
423 if handle_user_id:
424 # NOTE(marcink): we get new owner for objects...
424 # NOTE(marcink): we get new owner for objects...
425 handle_user = User.get_or_404(handle_user_id)
425 handle_user = User.get_or_404(handle_user_id)
426
426
427 if _repos and self.request.POST.get('user_repos'):
427 if _repos and self.request.POST.get('user_repos'):
428 handle_repos = self.request.POST['user_repos']
428 handle_repos = self.request.POST['user_repos']
429
429
430 if _repo_groups and self.request.POST.get('user_repo_groups'):
430 if _repo_groups and self.request.POST.get('user_repo_groups'):
431 handle_repo_groups = self.request.POST['user_repo_groups']
431 handle_repo_groups = self.request.POST['user_repo_groups']
432
432
433 if _user_groups and self.request.POST.get('user_user_groups'):
433 if _user_groups and self.request.POST.get('user_user_groups'):
434 handle_user_groups = self.request.POST['user_user_groups']
434 handle_user_groups = self.request.POST['user_user_groups']
435
435
436 if _pull_requests and self.request.POST.get('user_pull_requests'):
436 if _pull_requests and self.request.POST.get('user_pull_requests'):
437 handle_pull_requests = self.request.POST['user_pull_requests']
437 handle_pull_requests = self.request.POST['user_pull_requests']
438
438
439 if _artifacts and self.request.POST.get('user_artifacts'):
439 if _artifacts and self.request.POST.get('user_artifacts'):
440 handle_artifacts = self.request.POST['user_artifacts']
440 handle_artifacts = self.request.POST['user_artifacts']
441
441
442 old_values = c.user.get_api_data()
442 old_values = c.user.get_api_data()
443
443
444 try:
444 try:
445
445
446 UserModel().delete(
446 UserModel().delete(
447 c.user,
447 c.user,
448 handle_repos=handle_repos,
448 handle_repos=handle_repos,
449 handle_repo_groups=handle_repo_groups,
449 handle_repo_groups=handle_repo_groups,
450 handle_user_groups=handle_user_groups,
450 handle_user_groups=handle_user_groups,
451 handle_pull_requests=handle_pull_requests,
451 handle_pull_requests=handle_pull_requests,
452 handle_artifacts=handle_artifacts,
452 handle_artifacts=handle_artifacts,
453 handle_new_owner=handle_user
453 handle_new_owner=handle_user
454 )
454 )
455
455
456 audit_logger.store_web(
456 audit_logger.store_web(
457 'user.delete', action_data={'old_data': old_values},
457 'user.delete', action_data={'old_data': old_values},
458 user=c.rhodecode_user)
458 user=c.rhodecode_user)
459
459
460 Session().commit()
460 Session().commit()
461 set_handle_flash_repos()
461 set_handle_flash_repos()
462 set_handle_flash_repo_groups()
462 set_handle_flash_repo_groups()
463 set_handle_flash_user_groups()
463 set_handle_flash_user_groups()
464 set_handle_flash_pull_requests()
464 set_handle_flash_pull_requests()
465 set_handle_flash_artifacts()
465 set_handle_flash_artifacts()
466 username = h.escape(old_values['username'])
466 username = h.escape(old_values['username'])
467 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
467 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
468 except (UserOwnsReposException, UserOwnsRepoGroupsException,
468 except (UserOwnsReposException, UserOwnsRepoGroupsException,
469 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
469 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
470 UserOwnsArtifactsException, DefaultUserException) as e:
470 UserOwnsArtifactsException, DefaultUserException) as e:
471
471
472 h.flash(safe_str(e), category='warning')
472 h.flash(safe_str(e), category='warning')
473 except Exception:
473 except Exception:
474 log.exception("Exception during deletion of user")
474 log.exception("Exception during deletion of user")
475 h.flash(_('An error occurred during deletion of user'),
475 h.flash(_('An error occurred during deletion of user'),
476 category='error')
476 category='error')
477 raise HTTPFound(h.route_path('users'))
477 raise HTTPFound(h.route_path('users'))
478
478
479 @LoginRequired()
479 @LoginRequired()
480 @HasPermissionAllDecorator('hg.admin')
480 @HasPermissionAllDecorator('hg.admin')
481 def user_edit(self):
481 def user_edit(self):
482 _ = self.request.translate
482 _ = self.request.translate
483 c = self.load_default_context()
483 c = self.load_default_context()
484 c.user = self.db_user
484 c.user = self.db_user
485
485
486 c.active = 'profile'
486 c.active = 'profile'
487 c.extern_type = c.user.extern_type
487 c.extern_type = c.user.extern_type
488 c.extern_name = c.user.extern_name
488 c.extern_name = c.user.extern_name
489 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
489 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
490 c.edit_mode = self.request.GET.get('edit') == '1'
490 c.edit_mode = self.request.GET.get('edit') == '1'
491
491
492 defaults = c.user.get_dict()
492 defaults = c.user.get_dict()
493 defaults.update({'language': c.user.user_data.get('language')})
493 defaults.update({'language': c.user.user_data.get('language')})
494
494
495 data = render(
495 data = render(
496 'rhodecode:templates/admin/users/user_edit.mako',
496 'rhodecode:templates/admin/users/user_edit.mako',
497 self._get_template_context(c), self.request)
497 self._get_template_context(c), self.request)
498 html = formencode.htmlfill.render(
498 html = formencode.htmlfill.render(
499 data,
499 data,
500 defaults=defaults,
500 defaults=defaults,
501 encoding="UTF-8",
501 encoding="UTF-8",
502 force_defaults=False
502 force_defaults=False
503 )
503 )
504 return Response(html)
504 return Response(html)
505
505
506 @LoginRequired()
506 @LoginRequired()
507 @HasPermissionAllDecorator('hg.admin')
507 @HasPermissionAllDecorator('hg.admin')
508 def user_edit_advanced(self):
508 def user_edit_advanced(self):
509 _ = self.request.translate
509 _ = self.request.translate
510 c = self.load_default_context()
510 c = self.load_default_context()
511
511
512 user_id = self.db_user_id
512 user_id = self.db_user_id
513 c.user = self.db_user
513 c.user = self.db_user
514
514
515 c.detach_user = User.get_first_super_admin()
515 c.detach_user = User.get_first_super_admin()
516 detach_user_id = safe_int(self.request.GET.get('detach_user_id'))
516 detach_user_id = safe_int(self.request.GET.get('detach_user_id'))
517 if detach_user_id:
517 if detach_user_id:
518 c.detach_user = User.get_or_404(detach_user_id)
518 c.detach_user = User.get_or_404(detach_user_id)
519
519
520 c.active = 'advanced'
520 c.active = 'advanced'
521 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
521 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
522 c.personal_repo_group_name = RepoGroupModel()\
522 c.personal_repo_group_name = RepoGroupModel()\
523 .get_personal_group_name(c.user)
523 .get_personal_group_name(c.user)
524
524
525 c.user_to_review_rules = sorted(
525 c.user_to_review_rules = sorted(
526 (x.user for x in c.user.user_review_rules),
526 (x.user for x in c.user.user_review_rules),
527 key=lambda u: u.username.lower())
527 key=lambda u: u.username.lower())
528
528
529 defaults = c.user.get_dict()
529 defaults = c.user.get_dict()
530
530
531 # Interim workaround if the user participated on any pull requests as a
531 # Interim workaround if the user participated on any pull requests as a
532 # reviewer.
532 # reviewer.
533 has_review = len(c.user.reviewer_pull_requests)
533 has_review = len(c.user.reviewer_pull_requests)
534 c.can_delete_user = not has_review
534 c.can_delete_user = not has_review
535 c.can_delete_user_message = ''
535 c.can_delete_user_message = ''
536 inactive_link = h.link_to(
536 inactive_link = h.link_to(
537 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
537 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
538 if has_review == 1:
538 if has_review == 1:
539 c.can_delete_user_message = h.literal(_(
539 c.can_delete_user_message = h.literal(_(
540 'The user participates as reviewer in {} pull request and '
540 'The user participates as reviewer in {} pull request and '
541 'cannot be deleted. \nYou can set the user to '
541 'cannot be deleted. \nYou can set the user to '
542 '"{}" instead of deleting it.').format(
542 '"{}" instead of deleting it.').format(
543 has_review, inactive_link))
543 has_review, inactive_link))
544 elif has_review:
544 elif has_review:
545 c.can_delete_user_message = h.literal(_(
545 c.can_delete_user_message = h.literal(_(
546 'The user participates as reviewer in {} pull requests and '
546 'The user participates as reviewer in {} pull requests and '
547 'cannot be deleted. \nYou can set the user to '
547 'cannot be deleted. \nYou can set the user to '
548 '"{}" instead of deleting it.').format(
548 '"{}" instead of deleting it.').format(
549 has_review, inactive_link))
549 has_review, inactive_link))
550
550
551 data = render(
551 data = render(
552 'rhodecode:templates/admin/users/user_edit.mako',
552 'rhodecode:templates/admin/users/user_edit.mako',
553 self._get_template_context(c), self.request)
553 self._get_template_context(c), self.request)
554 html = formencode.htmlfill.render(
554 html = formencode.htmlfill.render(
555 data,
555 data,
556 defaults=defaults,
556 defaults=defaults,
557 encoding="UTF-8",
557 encoding="UTF-8",
558 force_defaults=False
558 force_defaults=False
559 )
559 )
560 return Response(html)
560 return Response(html)
561
561
562 @LoginRequired()
562 @LoginRequired()
563 @HasPermissionAllDecorator('hg.admin')
563 @HasPermissionAllDecorator('hg.admin')
564 def user_edit_global_perms(self):
564 def user_edit_global_perms(self):
565 _ = self.request.translate
565 _ = self.request.translate
566 c = self.load_default_context()
566 c = self.load_default_context()
567 c.user = self.db_user
567 c.user = self.db_user
568
568
569 c.active = 'global_perms'
569 c.active = 'global_perms'
570
570
571 c.default_user = User.get_default_user()
571 c.default_user = User.get_default_user()
572 defaults = c.user.get_dict()
572 defaults = c.user.get_dict()
573 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
573 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
574 defaults.update(c.default_user.get_default_perms())
574 defaults.update(c.default_user.get_default_perms())
575 defaults.update(c.user.get_default_perms())
575 defaults.update(c.user.get_default_perms())
576
576
577 data = render(
577 data = render(
578 'rhodecode:templates/admin/users/user_edit.mako',
578 'rhodecode:templates/admin/users/user_edit.mako',
579 self._get_template_context(c), self.request)
579 self._get_template_context(c), self.request)
580 html = formencode.htmlfill.render(
580 html = formencode.htmlfill.render(
581 data,
581 data,
582 defaults=defaults,
582 defaults=defaults,
583 encoding="UTF-8",
583 encoding="UTF-8",
584 force_defaults=False
584 force_defaults=False
585 )
585 )
586 return Response(html)
586 return Response(html)
587
587
588 @LoginRequired()
588 @LoginRequired()
589 @HasPermissionAllDecorator('hg.admin')
589 @HasPermissionAllDecorator('hg.admin')
590 @CSRFRequired()
590 @CSRFRequired()
591 def user_edit_global_perms_update(self):
591 def user_edit_global_perms_update(self):
592 _ = self.request.translate
592 _ = self.request.translate
593 c = self.load_default_context()
593 c = self.load_default_context()
594
594
595 user_id = self.db_user_id
595 user_id = self.db_user_id
596 c.user = self.db_user
596 c.user = self.db_user
597
597
598 c.active = 'global_perms'
598 c.active = 'global_perms'
599 try:
599 try:
600 # first stage that verifies the checkbox
600 # first stage that verifies the checkbox
601 _form = UserIndividualPermissionsForm(self.request.translate)
601 _form = UserIndividualPermissionsForm(self.request.translate)
602 form_result = _form.to_python(dict(self.request.POST))
602 form_result = _form.to_python(dict(self.request.POST))
603 inherit_perms = form_result['inherit_default_permissions']
603 inherit_perms = form_result['inherit_default_permissions']
604 c.user.inherit_default_permissions = inherit_perms
604 c.user.inherit_default_permissions = inherit_perms
605 Session().add(c.user)
605 Session().add(c.user)
606
606
607 if not inherit_perms:
607 if not inherit_perms:
608 # only update the individual ones if we un check the flag
608 # only update the individual ones if we un check the flag
609 _form = UserPermissionsForm(
609 _form = UserPermissionsForm(
610 self.request.translate,
610 self.request.translate,
611 [x[0] for x in c.repo_create_choices],
611 [x[0] for x in c.repo_create_choices],
612 [x[0] for x in c.repo_create_on_write_choices],
612 [x[0] for x in c.repo_create_on_write_choices],
613 [x[0] for x in c.repo_group_create_choices],
613 [x[0] for x in c.repo_group_create_choices],
614 [x[0] for x in c.user_group_create_choices],
614 [x[0] for x in c.user_group_create_choices],
615 [x[0] for x in c.fork_choices],
615 [x[0] for x in c.fork_choices],
616 [x[0] for x in c.inherit_default_permission_choices])()
616 [x[0] for x in c.inherit_default_permission_choices])()
617
617
618 form_result = _form.to_python(dict(self.request.POST))
618 form_result = _form.to_python(dict(self.request.POST))
619 form_result.update({'perm_user_id': c.user.user_id})
619 form_result.update({'perm_user_id': c.user.user_id})
620
620
621 PermissionModel().update_user_permissions(form_result)
621 PermissionModel().update_user_permissions(form_result)
622
622
623 # TODO(marcink): implement global permissions
623 # TODO(marcink): implement global permissions
624 # audit_log.store_web('user.edit.permissions')
624 # audit_log.store_web('user.edit.permissions')
625
625
626 Session().commit()
626 Session().commit()
627
627
628 h.flash(_('User global permissions updated successfully'),
628 h.flash(_('User global permissions updated successfully'),
629 category='success')
629 category='success')
630
630
631 except formencode.Invalid as errors:
631 except formencode.Invalid as errors:
632 data = render(
632 data = render(
633 'rhodecode:templates/admin/users/user_edit.mako',
633 'rhodecode:templates/admin/users/user_edit.mako',
634 self._get_template_context(c), self.request)
634 self._get_template_context(c), self.request)
635 html = formencode.htmlfill.render(
635 html = formencode.htmlfill.render(
636 data,
636 data,
637 defaults=errors.value,
637 defaults=errors.value,
638 errors=errors.unpack_errors() or {},
638 errors=errors.unpack_errors() or {},
639 prefix_error=False,
639 prefix_error=False,
640 encoding="UTF-8",
640 encoding="UTF-8",
641 force_defaults=False
641 force_defaults=False
642 )
642 )
643 return Response(html)
643 return Response(html)
644 except Exception:
644 except Exception:
645 log.exception("Exception during permissions saving")
645 log.exception("Exception during permissions saving")
646 h.flash(_('An error occurred during permissions saving'),
646 h.flash(_('An error occurred during permissions saving'),
647 category='error')
647 category='error')
648
648
649 affected_user_ids = [user_id]
649 affected_user_ids = [user_id]
650 PermissionModel().trigger_permission_flush(affected_user_ids)
650 PermissionModel().trigger_permission_flush(affected_user_ids)
651 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
651 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
652
652
653 @LoginRequired()
653 @LoginRequired()
654 @HasPermissionAllDecorator('hg.admin')
654 @HasPermissionAllDecorator('hg.admin')
655 @CSRFRequired()
655 @CSRFRequired()
656 def user_enable_force_password_reset(self):
656 def user_enable_force_password_reset(self):
657 _ = self.request.translate
657 _ = self.request.translate
658 c = self.load_default_context()
658 c = self.load_default_context()
659
659
660 user_id = self.db_user_id
660 user_id = self.db_user_id
661 c.user = self.db_user
661 c.user = self.db_user
662
662
663 try:
663 try:
664 c.user.update_userdata(force_password_change=True)
664 c.user.update_userdata(force_password_change=True)
665
665
666 msg = _('Force password change enabled for user')
666 msg = _('Force password change enabled for user')
667 audit_logger.store_web('user.edit.password_reset.enabled',
667 audit_logger.store_web('user.edit.password_reset.enabled',
668 user=c.rhodecode_user)
668 user=c.rhodecode_user)
669
669
670 Session().commit()
670 Session().commit()
671 h.flash(msg, category='success')
671 h.flash(msg, category='success')
672 except Exception:
672 except Exception:
673 log.exception("Exception during password reset for user")
673 log.exception("Exception during password reset for user")
674 h.flash(_('An error occurred during password reset for user'),
674 h.flash(_('An error occurred during password reset for user'),
675 category='error')
675 category='error')
676
676
677 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
677 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
678
678
679 @LoginRequired()
679 @LoginRequired()
680 @HasPermissionAllDecorator('hg.admin')
680 @HasPermissionAllDecorator('hg.admin')
681 @CSRFRequired()
681 @CSRFRequired()
682 def user_disable_force_password_reset(self):
682 def user_disable_force_password_reset(self):
683 _ = self.request.translate
683 _ = self.request.translate
684 c = self.load_default_context()
684 c = self.load_default_context()
685
685
686 user_id = self.db_user_id
686 user_id = self.db_user_id
687 c.user = self.db_user
687 c.user = self.db_user
688
688
689 try:
689 try:
690 c.user.update_userdata(force_password_change=False)
690 c.user.update_userdata(force_password_change=False)
691
691
692 msg = _('Force password change disabled for user')
692 msg = _('Force password change disabled for user')
693 audit_logger.store_web(
693 audit_logger.store_web(
694 'user.edit.password_reset.disabled',
694 'user.edit.password_reset.disabled',
695 user=c.rhodecode_user)
695 user=c.rhodecode_user)
696
696
697 Session().commit()
697 Session().commit()
698 h.flash(msg, category='success')
698 h.flash(msg, category='success')
699 except Exception:
699 except Exception:
700 log.exception("Exception during password reset for user")
700 log.exception("Exception during password reset for user")
701 h.flash(_('An error occurred during password reset for user'),
701 h.flash(_('An error occurred during password reset for user'),
702 category='error')
702 category='error')
703
703
704 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
704 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
705
705
706 @LoginRequired()
706 @LoginRequired()
707 @HasPermissionAllDecorator('hg.admin')
707 @HasPermissionAllDecorator('hg.admin')
708 @CSRFRequired()
708 @CSRFRequired()
709 def user_notice_dismiss(self):
709 def user_notice_dismiss(self):
710 _ = self.request.translate
710 _ = self.request.translate
711 c = self.load_default_context()
711 c = self.load_default_context()
712
712
713 user_id = self.db_user_id
713 user_id = self.db_user_id
714 c.user = self.db_user
714 c.user = self.db_user
715 user_notice_id = safe_int(self.request.POST.get('notice_id'))
715 user_notice_id = safe_int(self.request.POST.get('notice_id'))
716 notice = UserNotice().query()\
716 notice = UserNotice().query()\
717 .filter(UserNotice.user_id == user_id)\
717 .filter(UserNotice.user_id == user_id)\
718 .filter(UserNotice.user_notice_id == user_notice_id)\
718 .filter(UserNotice.user_notice_id == user_notice_id)\
719 .scalar()
719 .scalar()
720 read = False
720 read = False
721 if notice:
721 if notice:
722 notice.notice_read = True
722 notice.notice_read = True
723 Session().add(notice)
723 Session().add(notice)
724 Session().commit()
724 Session().commit()
725 read = True
725 read = True
726
726
727 return {'notice': user_notice_id, 'read': read}
727 return {'notice': user_notice_id, 'read': read}
728
728
729 @LoginRequired()
729 @LoginRequired()
730 @HasPermissionAllDecorator('hg.admin')
730 @HasPermissionAllDecorator('hg.admin')
731 @CSRFRequired()
731 @CSRFRequired()
732 def user_create_personal_repo_group(self):
732 def user_create_personal_repo_group(self):
733 """
733 """
734 Create personal repository group for this user
734 Create personal repository group for this user
735 """
735 """
736 from rhodecode.model.repo_group import RepoGroupModel
736 from rhodecode.model.repo_group import RepoGroupModel
737
737
738 _ = self.request.translate
738 _ = self.request.translate
739 c = self.load_default_context()
739 c = self.load_default_context()
740
740
741 user_id = self.db_user_id
741 user_id = self.db_user_id
742 c.user = self.db_user
742 c.user = self.db_user
743
743
744 personal_repo_group = RepoGroup.get_user_personal_repo_group(
744 personal_repo_group = RepoGroup.get_user_personal_repo_group(
745 c.user.user_id)
745 c.user.user_id)
746 if personal_repo_group:
746 if personal_repo_group:
747 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
747 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
748
748
749 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
749 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
750 named_personal_group = RepoGroup.get_by_group_name(
750 named_personal_group = RepoGroup.get_by_group_name(
751 personal_repo_group_name)
751 personal_repo_group_name)
752 try:
752 try:
753
753
754 if named_personal_group and named_personal_group.user_id == c.user.user_id:
754 if named_personal_group and named_personal_group.user_id == c.user.user_id:
755 # migrate the same named group, and mark it as personal
755 # migrate the same named group, and mark it as personal
756 named_personal_group.personal = True
756 named_personal_group.personal = True
757 Session().add(named_personal_group)
757 Session().add(named_personal_group)
758 Session().commit()
758 Session().commit()
759 msg = _('Linked repository group `{}` as personal'.format(
759 msg = _('Linked repository group `{}` as personal'.format(
760 personal_repo_group_name))
760 personal_repo_group_name))
761 h.flash(msg, category='success')
761 h.flash(msg, category='success')
762 elif not named_personal_group:
762 elif not named_personal_group:
763 RepoGroupModel().create_personal_repo_group(c.user)
763 RepoGroupModel().create_personal_repo_group(c.user)
764
764
765 msg = _('Created repository group `{}`'.format(
765 msg = _('Created repository group `{}`'.format(
766 personal_repo_group_name))
766 personal_repo_group_name))
767 h.flash(msg, category='success')
767 h.flash(msg, category='success')
768 else:
768 else:
769 msg = _('Repository group `{}` is already taken'.format(
769 msg = _('Repository group `{}` is already taken'.format(
770 personal_repo_group_name))
770 personal_repo_group_name))
771 h.flash(msg, category='warning')
771 h.flash(msg, category='warning')
772 except Exception:
772 except Exception:
773 log.exception("Exception during repository group creation")
773 log.exception("Exception during repository group creation")
774 msg = _(
774 msg = _(
775 'An error occurred during repository group creation for user')
775 'An error occurred during repository group creation for user')
776 h.flash(msg, category='error')
776 h.flash(msg, category='error')
777 Session().rollback()
777 Session().rollback()
778
778
779 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
779 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
780
780
781 @LoginRequired()
781 @LoginRequired()
782 @HasPermissionAllDecorator('hg.admin')
782 @HasPermissionAllDecorator('hg.admin')
783 def auth_tokens(self):
783 def auth_tokens(self):
784 _ = self.request.translate
784 _ = self.request.translate
785 c = self.load_default_context()
785 c = self.load_default_context()
786 c.user = self.db_user
786 c.user = self.db_user
787
787
788 c.active = 'auth_tokens'
788 c.active = 'auth_tokens'
789
789
790 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
790 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
791 c.role_values = [
791 c.role_values = [
792 (x, AuthTokenModel.cls._get_role_name(x))
792 (x, AuthTokenModel.cls._get_role_name(x))
793 for x in AuthTokenModel.cls.ROLES]
793 for x in AuthTokenModel.cls.ROLES]
794 c.role_options = [(c.role_values, _("Role"))]
794 c.role_options = [(c.role_values, _("Role"))]
795 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
795 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
796 c.user.user_id, show_expired=True)
796 c.user.user_id, show_expired=True)
797 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
797 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
798 return self._get_template_context(c)
798 return self._get_template_context(c)
799
799
800 @LoginRequired()
800 @LoginRequired()
801 @HasPermissionAllDecorator('hg.admin')
801 @HasPermissionAllDecorator('hg.admin')
802 def auth_tokens_view(self):
802 def auth_tokens_view(self):
803 _ = self.request.translate
803 _ = self.request.translate
804 c = self.load_default_context()
804 c = self.load_default_context()
805 c.user = self.db_user
805 c.user = self.db_user
806
806
807 auth_token_id = self.request.POST.get('auth_token_id')
807 auth_token_id = self.request.POST.get('auth_token_id')
808
808
809 if auth_token_id:
809 if auth_token_id:
810 token = UserApiKeys.get_or_404(auth_token_id)
810 token = UserApiKeys.get_or_404(auth_token_id)
811
811
812 return {
812 return {
813 'auth_token': token.api_key
813 'auth_token': token.api_key
814 }
814 }
815
815
816 def maybe_attach_token_scope(self, token):
816 def maybe_attach_token_scope(self, token):
817 # implemented in EE edition
817 # implemented in EE edition
818 pass
818 pass
819
819
820 @LoginRequired()
820 @LoginRequired()
821 @HasPermissionAllDecorator('hg.admin')
821 @HasPermissionAllDecorator('hg.admin')
822 @CSRFRequired()
822 @CSRFRequired()
823 def auth_tokens_add(self):
823 def auth_tokens_add(self):
824 _ = self.request.translate
824 _ = self.request.translate
825 c = self.load_default_context()
825 c = self.load_default_context()
826
826
827 user_id = self.db_user_id
827 user_id = self.db_user_id
828 c.user = self.db_user
828 c.user = self.db_user
829
829
830 user_data = c.user.get_api_data()
830 user_data = c.user.get_api_data()
831 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
831 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
832 description = self.request.POST.get('description')
832 description = self.request.POST.get('description')
833 role = self.request.POST.get('role')
833 role = self.request.POST.get('role')
834
834
835 token = UserModel().add_auth_token(
835 token = UserModel().add_auth_token(
836 user=c.user.user_id,
836 user=c.user.user_id,
837 lifetime_minutes=lifetime, role=role, description=description,
837 lifetime_minutes=lifetime, role=role, description=description,
838 scope_callback=self.maybe_attach_token_scope)
838 scope_callback=self.maybe_attach_token_scope)
839 token_data = token.get_api_data()
839 token_data = token.get_api_data()
840
840
841 audit_logger.store_web(
841 audit_logger.store_web(
842 'user.edit.token.add', action_data={
842 'user.edit.token.add', action_data={
843 'data': {'token': token_data, 'user': user_data}},
843 'data': {'token': token_data, 'user': user_data}},
844 user=self._rhodecode_user, )
844 user=self._rhodecode_user, )
845 Session().commit()
845 Session().commit()
846
846
847 h.flash(_("Auth token successfully created"), category='success')
847 h.flash(_("Auth token successfully created"), category='success')
848 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
848 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
849
849
850 @LoginRequired()
850 @LoginRequired()
851 @HasPermissionAllDecorator('hg.admin')
851 @HasPermissionAllDecorator('hg.admin')
852 @CSRFRequired()
852 @CSRFRequired()
853 def auth_tokens_delete(self):
853 def auth_tokens_delete(self):
854 _ = self.request.translate
854 _ = self.request.translate
855 c = self.load_default_context()
855 c = self.load_default_context()
856
856
857 user_id = self.db_user_id
857 user_id = self.db_user_id
858 c.user = self.db_user
858 c.user = self.db_user
859
859
860 user_data = c.user.get_api_data()
860 user_data = c.user.get_api_data()
861
861
862 del_auth_token = self.request.POST.get('del_auth_token')
862 del_auth_token = self.request.POST.get('del_auth_token')
863
863
864 if del_auth_token:
864 if del_auth_token:
865 token = UserApiKeys.get_or_404(del_auth_token)
865 token = UserApiKeys.get_or_404(del_auth_token)
866 token_data = token.get_api_data()
866 token_data = token.get_api_data()
867
867
868 AuthTokenModel().delete(del_auth_token, c.user.user_id)
868 AuthTokenModel().delete(del_auth_token, c.user.user_id)
869 audit_logger.store_web(
869 audit_logger.store_web(
870 'user.edit.token.delete', action_data={
870 'user.edit.token.delete', action_data={
871 'data': {'token': token_data, 'user': user_data}},
871 'data': {'token': token_data, 'user': user_data}},
872 user=self._rhodecode_user,)
872 user=self._rhodecode_user,)
873 Session().commit()
873 Session().commit()
874 h.flash(_("Auth token successfully deleted"), category='success')
874 h.flash(_("Auth token successfully deleted"), category='success')
875
875
876 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
876 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
877
877
878 @LoginRequired()
878 @LoginRequired()
879 @HasPermissionAllDecorator('hg.admin')
879 @HasPermissionAllDecorator('hg.admin')
880 def ssh_keys(self):
880 def ssh_keys(self):
881 _ = self.request.translate
881 _ = self.request.translate
882 c = self.load_default_context()
882 c = self.load_default_context()
883 c.user = self.db_user
883 c.user = self.db_user
884
884
885 c.active = 'ssh_keys'
885 c.active = 'ssh_keys'
886 c.default_key = self.request.GET.get('default_key')
886 c.default_key = self.request.GET.get('default_key')
887 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
887 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
888 return self._get_template_context(c)
888 return self._get_template_context(c)
889
889
890 @LoginRequired()
890 @LoginRequired()
891 @HasPermissionAllDecorator('hg.admin')
891 @HasPermissionAllDecorator('hg.admin')
892 def ssh_keys_generate_keypair(self):
892 def ssh_keys_generate_keypair(self):
893 _ = self.request.translate
893 _ = self.request.translate
894 c = self.load_default_context()
894 c = self.load_default_context()
895
895
896 c.user = self.db_user
896 c.user = self.db_user
897
897
898 c.active = 'ssh_keys_generate'
898 c.active = 'ssh_keys_generate'
899 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
899 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
900 private_format = self.request.GET.get('private_format') \
900 private_format = self.request.GET.get('private_format') \
901 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
901 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
902 c.private, c.public = SshKeyModel().generate_keypair(
902 c.private, c.public = SshKeyModel().generate_keypair(
903 comment=comment, private_format=private_format)
903 comment=comment, private_format=private_format)
904
904
905 return self._get_template_context(c)
905 return self._get_template_context(c)
906
906
907 @LoginRequired()
907 @LoginRequired()
908 @HasPermissionAllDecorator('hg.admin')
908 @HasPermissionAllDecorator('hg.admin')
909 @CSRFRequired()
909 @CSRFRequired()
910 def ssh_keys_add(self):
910 def ssh_keys_add(self):
911 _ = self.request.translate
911 _ = self.request.translate
912 c = self.load_default_context()
912 c = self.load_default_context()
913
913
914 user_id = self.db_user_id
914 user_id = self.db_user_id
915 c.user = self.db_user
915 c.user = self.db_user
916
916
917 user_data = c.user.get_api_data()
917 user_data = c.user.get_api_data()
918 key_data = self.request.POST.get('key_data')
918 key_data = self.request.POST.get('key_data')
919 description = self.request.POST.get('description')
919 description = self.request.POST.get('description')
920
920
921 fingerprint = 'unknown'
921 fingerprint = 'unknown'
922 try:
922 try:
923 if not key_data:
923 if not key_data:
924 raise ValueError('Please add a valid public key')
924 raise ValueError('Please add a valid public key')
925
925
926 key = SshKeyModel().parse_key(key_data.strip())
926 key = SshKeyModel().parse_key(key_data.strip())
927 fingerprint = key.hash_md5()
927 fingerprint = key.hash_md5()
928
928
929 ssh_key = SshKeyModel().create(
929 ssh_key = SshKeyModel().create(
930 c.user.user_id, fingerprint, key.keydata, description)
930 c.user.user_id, fingerprint, key.keydata, description)
931 ssh_key_data = ssh_key.get_api_data()
931 ssh_key_data = ssh_key.get_api_data()
932
932
933 audit_logger.store_web(
933 audit_logger.store_web(
934 'user.edit.ssh_key.add', action_data={
934 'user.edit.ssh_key.add', action_data={
935 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
935 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
936 user=self._rhodecode_user, )
936 user=self._rhodecode_user, )
937 Session().commit()
937 Session().commit()
938
938
939 # Trigger an event on change of keys.
939 # Trigger an event on change of keys.
940 trigger(SshKeyFileChangeEvent(), self.request.registry)
940 trigger(SshKeyFileChangeEvent(), self.request.registry)
941
941
942 h.flash(_("Ssh Key successfully created"), category='success')
942 h.flash(_("Ssh Key successfully created"), category='success')
943
943
944 except IntegrityError:
944 except IntegrityError:
945 log.exception("Exception during ssh key saving")
945 log.exception("Exception during ssh key saving")
946 err = 'Such key with fingerprint `{}` already exists, ' \
946 err = 'Such key with fingerprint `{}` already exists, ' \
947 'please use a different one'.format(fingerprint)
947 'please use a different one'.format(fingerprint)
948 h.flash(_('An error occurred during ssh key saving: {}').format(err),
948 h.flash(_('An error occurred during ssh key saving: {}').format(err),
949 category='error')
949 category='error')
950 except Exception as e:
950 except Exception as e:
951 log.exception("Exception during ssh key saving")
951 log.exception("Exception during ssh key saving")
952 h.flash(_('An error occurred during ssh key saving: {}').format(e),
952 h.flash(_('An error occurred during ssh key saving: {}').format(e),
953 category='error')
953 category='error')
954
954
955 return HTTPFound(
955 return HTTPFound(
956 h.route_path('edit_user_ssh_keys', user_id=user_id))
956 h.route_path('edit_user_ssh_keys', user_id=user_id))
957
957
958 @LoginRequired()
958 @LoginRequired()
959 @HasPermissionAllDecorator('hg.admin')
959 @HasPermissionAllDecorator('hg.admin')
960 @CSRFRequired()
960 @CSRFRequired()
961 def ssh_keys_delete(self):
961 def ssh_keys_delete(self):
962 _ = self.request.translate
962 _ = self.request.translate
963 c = self.load_default_context()
963 c = self.load_default_context()
964
964
965 user_id = self.db_user_id
965 user_id = self.db_user_id
966 c.user = self.db_user
966 c.user = self.db_user
967
967
968 user_data = c.user.get_api_data()
968 user_data = c.user.get_api_data()
969
969
970 del_ssh_key = self.request.POST.get('del_ssh_key')
970 del_ssh_key = self.request.POST.get('del_ssh_key')
971
971
972 if del_ssh_key:
972 if del_ssh_key:
973 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
973 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
974 ssh_key_data = ssh_key.get_api_data()
974 ssh_key_data = ssh_key.get_api_data()
975
975
976 SshKeyModel().delete(del_ssh_key, c.user.user_id)
976 SshKeyModel().delete(del_ssh_key, c.user.user_id)
977 audit_logger.store_web(
977 audit_logger.store_web(
978 'user.edit.ssh_key.delete', action_data={
978 'user.edit.ssh_key.delete', action_data={
979 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
979 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
980 user=self._rhodecode_user,)
980 user=self._rhodecode_user,)
981 Session().commit()
981 Session().commit()
982 # Trigger an event on change of keys.
982 # Trigger an event on change of keys.
983 trigger(SshKeyFileChangeEvent(), self.request.registry)
983 trigger(SshKeyFileChangeEvent(), self.request.registry)
984 h.flash(_("Ssh key successfully deleted"), category='success')
984 h.flash(_("Ssh key successfully deleted"), category='success')
985
985
986 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
986 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
987
987
988 @LoginRequired()
988 @LoginRequired()
989 @HasPermissionAllDecorator('hg.admin')
989 @HasPermissionAllDecorator('hg.admin')
990 def emails(self):
990 def emails(self):
991 _ = self.request.translate
991 _ = self.request.translate
992 c = self.load_default_context()
992 c = self.load_default_context()
993 c.user = self.db_user
993 c.user = self.db_user
994
994
995 c.active = 'emails'
995 c.active = 'emails'
996 c.user_email_map = UserEmailMap.query() \
996 c.user_email_map = UserEmailMap.query() \
997 .filter(UserEmailMap.user == c.user).all()
997 .filter(UserEmailMap.user == c.user).all()
998
998
999 return self._get_template_context(c)
999 return self._get_template_context(c)
1000
1000
1001 @LoginRequired()
1001 @LoginRequired()
1002 @HasPermissionAllDecorator('hg.admin')
1002 @HasPermissionAllDecorator('hg.admin')
1003 @CSRFRequired()
1003 @CSRFRequired()
1004 def emails_add(self):
1004 def emails_add(self):
1005 _ = self.request.translate
1005 _ = self.request.translate
1006 c = self.load_default_context()
1006 c = self.load_default_context()
1007
1007
1008 user_id = self.db_user_id
1008 user_id = self.db_user_id
1009 c.user = self.db_user
1009 c.user = self.db_user
1010
1010
1011 email = self.request.POST.get('new_email')
1011 email = self.request.POST.get('new_email')
1012 user_data = c.user.get_api_data()
1012 user_data = c.user.get_api_data()
1013 try:
1013 try:
1014
1014
1015 form = UserExtraEmailForm(self.request.translate)()
1015 form = UserExtraEmailForm(self.request.translate)()
1016 data = form.to_python({'email': email})
1016 data = form.to_python({'email': email})
1017 email = data['email']
1017 email = data['email']
1018
1018
1019 UserModel().add_extra_email(c.user.user_id, email)
1019 UserModel().add_extra_email(c.user.user_id, email)
1020 audit_logger.store_web(
1020 audit_logger.store_web(
1021 'user.edit.email.add',
1021 'user.edit.email.add',
1022 action_data={'email': email, 'user': user_data},
1022 action_data={'email': email, 'user': user_data},
1023 user=self._rhodecode_user)
1023 user=self._rhodecode_user)
1024 Session().commit()
1024 Session().commit()
1025 h.flash(_("Added new email address `%s` for user account") % email,
1025 h.flash(_("Added new email address `%s` for user account") % email,
1026 category='success')
1026 category='success')
1027 except formencode.Invalid as error:
1027 except formencode.Invalid as error:
1028 msg = error.unpack_errors()['email']
1028 msg = error.unpack_errors()['email']
1029 h.flash(h.escape(msg), category='error')
1029 h.flash(h.escape(msg), category='error')
1030 except IntegrityError:
1030 except IntegrityError:
1031 log.warning("Email %s already exists", email)
1031 log.warning("Email %s already exists", email)
1032 h.flash(_('Email `{}` is already registered for another user.').format(email),
1032 h.flash(_('Email `{}` is already registered for another user.').format(email),
1033 category='error')
1033 category='error')
1034 except Exception:
1034 except Exception:
1035 log.exception("Exception during email saving")
1035 log.exception("Exception during email saving")
1036 h.flash(_('An error occurred during email saving'),
1036 h.flash(_('An error occurred during email saving'),
1037 category='error')
1037 category='error')
1038 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1038 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1039
1039
1040 @LoginRequired()
1040 @LoginRequired()
1041 @HasPermissionAllDecorator('hg.admin')
1041 @HasPermissionAllDecorator('hg.admin')
1042 @CSRFRequired()
1042 @CSRFRequired()
1043 def emails_delete(self):
1043 def emails_delete(self):
1044 _ = self.request.translate
1044 _ = self.request.translate
1045 c = self.load_default_context()
1045 c = self.load_default_context()
1046
1046
1047 user_id = self.db_user_id
1047 user_id = self.db_user_id
1048 c.user = self.db_user
1048 c.user = self.db_user
1049
1049
1050 email_id = self.request.POST.get('del_email_id')
1050 email_id = self.request.POST.get('del_email_id')
1051 user_model = UserModel()
1051 user_model = UserModel()
1052
1052
1053 email = UserEmailMap.query().get(email_id).email
1053 email = UserEmailMap.query().get(email_id).email
1054 user_data = c.user.get_api_data()
1054 user_data = c.user.get_api_data()
1055 user_model.delete_extra_email(c.user.user_id, email_id)
1055 user_model.delete_extra_email(c.user.user_id, email_id)
1056 audit_logger.store_web(
1056 audit_logger.store_web(
1057 'user.edit.email.delete',
1057 'user.edit.email.delete',
1058 action_data={'email': email, 'user': user_data},
1058 action_data={'email': email, 'user': user_data},
1059 user=self._rhodecode_user)
1059 user=self._rhodecode_user)
1060 Session().commit()
1060 Session().commit()
1061 h.flash(_("Removed email address from user account"),
1061 h.flash(_("Removed email address from user account"),
1062 category='success')
1062 category='success')
1063 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1063 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1064
1064
1065 @LoginRequired()
1065 @LoginRequired()
1066 @HasPermissionAllDecorator('hg.admin')
1066 @HasPermissionAllDecorator('hg.admin')
1067 def ips(self):
1067 def ips(self):
1068 _ = self.request.translate
1068 _ = self.request.translate
1069 c = self.load_default_context()
1069 c = self.load_default_context()
1070 c.user = self.db_user
1070 c.user = self.db_user
1071
1071
1072 c.active = 'ips'
1072 c.active = 'ips'
1073 c.user_ip_map = UserIpMap.query() \
1073 c.user_ip_map = UserIpMap.query() \
1074 .filter(UserIpMap.user == c.user).all()
1074 .filter(UserIpMap.user == c.user).all()
1075
1075
1076 c.inherit_default_ips = c.user.inherit_default_permissions
1076 c.inherit_default_ips = c.user.inherit_default_permissions
1077 c.default_user_ip_map = UserIpMap.query() \
1077 c.default_user_ip_map = UserIpMap.query() \
1078 .filter(UserIpMap.user == User.get_default_user()).all()
1078 .filter(UserIpMap.user == User.get_default_user()).all()
1079
1079
1080 return self._get_template_context(c)
1080 return self._get_template_context(c)
1081
1081
1082 @LoginRequired()
1082 @LoginRequired()
1083 @HasPermissionAllDecorator('hg.admin')
1083 @HasPermissionAllDecorator('hg.admin')
1084 @CSRFRequired()
1084 @CSRFRequired()
1085 # NOTE(marcink): this view is allowed for default users, as we can
1085 # NOTE(marcink): this view is allowed for default users, as we can
1086 # edit their IP white list
1086 # edit their IP white list
1087 def ips_add(self):
1087 def ips_add(self):
1088 _ = self.request.translate
1088 _ = self.request.translate
1089 c = self.load_default_context()
1089 c = self.load_default_context()
1090
1090
1091 user_id = self.db_user_id
1091 user_id = self.db_user_id
1092 c.user = self.db_user
1092 c.user = self.db_user
1093
1093
1094 user_model = UserModel()
1094 user_model = UserModel()
1095 desc = self.request.POST.get('description')
1095 desc = self.request.POST.get('description')
1096 try:
1096 try:
1097 ip_list = user_model.parse_ip_range(
1097 ip_list = user_model.parse_ip_range(
1098 self.request.POST.get('new_ip'))
1098 self.request.POST.get('new_ip'))
1099 except Exception as e:
1099 except Exception as e:
1100 ip_list = []
1100 ip_list = []
1101 log.exception("Exception during ip saving")
1101 log.exception("Exception during ip saving")
1102 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1102 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1103 category='error')
1103 category='error')
1104 added = []
1104 added = []
1105 user_data = c.user.get_api_data()
1105 user_data = c.user.get_api_data()
1106 for ip in ip_list:
1106 for ip in ip_list:
1107 try:
1107 try:
1108 form = UserExtraIpForm(self.request.translate)()
1108 form = UserExtraIpForm(self.request.translate)()
1109 data = form.to_python({'ip': ip})
1109 data = form.to_python({'ip': ip})
1110 ip = data['ip']
1110 ip = data['ip']
1111
1111
1112 user_model.add_extra_ip(c.user.user_id, ip, desc)
1112 user_model.add_extra_ip(c.user.user_id, ip, desc)
1113 audit_logger.store_web(
1113 audit_logger.store_web(
1114 'user.edit.ip.add',
1114 'user.edit.ip.add',
1115 action_data={'ip': ip, 'user': user_data},
1115 action_data={'ip': ip, 'user': user_data},
1116 user=self._rhodecode_user)
1116 user=self._rhodecode_user)
1117 Session().commit()
1117 Session().commit()
1118 added.append(ip)
1118 added.append(ip)
1119 except formencode.Invalid as error:
1119 except formencode.Invalid as error:
1120 msg = error.unpack_errors()['ip']
1120 msg = error.unpack_errors()['ip']
1121 h.flash(msg, category='error')
1121 h.flash(msg, category='error')
1122 except Exception:
1122 except Exception:
1123 log.exception("Exception during ip saving")
1123 log.exception("Exception during ip saving")
1124 h.flash(_('An error occurred during ip saving'),
1124 h.flash(_('An error occurred during ip saving'),
1125 category='error')
1125 category='error')
1126 if added:
1126 if added:
1127 h.flash(
1127 h.flash(
1128 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1128 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1129 category='success')
1129 category='success')
1130 if 'default_user' in self.request.POST:
1130 if 'default_user' in self.request.POST:
1131 # case for editing global IP list we do it for 'DEFAULT' user
1131 # case for editing global IP list we do it for 'DEFAULT' user
1132 raise HTTPFound(h.route_path('admin_permissions_ips'))
1132 raise HTTPFound(h.route_path('admin_permissions_ips'))
1133 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1133 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1134
1134
1135 @LoginRequired()
1135 @LoginRequired()
1136 @HasPermissionAllDecorator('hg.admin')
1136 @HasPermissionAllDecorator('hg.admin')
1137 @CSRFRequired()
1137 @CSRFRequired()
1138 # NOTE(marcink): this view is allowed for default users, as we can
1138 # NOTE(marcink): this view is allowed for default users, as we can
1139 # edit their IP white list
1139 # edit their IP white list
1140 def ips_delete(self):
1140 def ips_delete(self):
1141 _ = self.request.translate
1141 _ = self.request.translate
1142 c = self.load_default_context()
1142 c = self.load_default_context()
1143
1143
1144 user_id = self.db_user_id
1144 user_id = self.db_user_id
1145 c.user = self.db_user
1145 c.user = self.db_user
1146
1146
1147 ip_id = self.request.POST.get('del_ip_id')
1147 ip_id = self.request.POST.get('del_ip_id')
1148 user_model = UserModel()
1148 user_model = UserModel()
1149 user_data = c.user.get_api_data()
1149 user_data = c.user.get_api_data()
1150 ip = UserIpMap.query().get(ip_id).ip_addr
1150 ip = UserIpMap.query().get(ip_id).ip_addr
1151 user_model.delete_extra_ip(c.user.user_id, ip_id)
1151 user_model.delete_extra_ip(c.user.user_id, ip_id)
1152 audit_logger.store_web(
1152 audit_logger.store_web(
1153 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1153 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1154 user=self._rhodecode_user)
1154 user=self._rhodecode_user)
1155 Session().commit()
1155 Session().commit()
1156 h.flash(_("Removed ip address from user whitelist"), category='success')
1156 h.flash(_("Removed ip address from user whitelist"), category='success')
1157
1157
1158 if 'default_user' in self.request.POST:
1158 if 'default_user' in self.request.POST:
1159 # case for editing global IP list we do it for 'DEFAULT' user
1159 # case for editing global IP list we do it for 'DEFAULT' user
1160 raise HTTPFound(h.route_path('admin_permissions_ips'))
1160 raise HTTPFound(h.route_path('admin_permissions_ips'))
1161 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1161 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1162
1162
1163 @LoginRequired()
1163 @LoginRequired()
1164 @HasPermissionAllDecorator('hg.admin')
1164 @HasPermissionAllDecorator('hg.admin')
1165 def groups_management(self):
1165 def groups_management(self):
1166 c = self.load_default_context()
1166 c = self.load_default_context()
1167 c.user = self.db_user
1167 c.user = self.db_user
1168 c.data = c.user.group_member
1168 c.data = c.user.group_member
1169
1169
1170 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1170 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1171 for group in c.user.group_member]
1171 for group in c.user.group_member]
1172 c.groups = ext_json.str_json(groups)
1172 c.groups = ext_json.str_json(groups)
1173 c.active = 'groups'
1173 c.active = 'groups'
1174
1174
1175 return self._get_template_context(c)
1175 return self._get_template_context(c)
1176
1176
1177 @LoginRequired()
1177 @LoginRequired()
1178 @HasPermissionAllDecorator('hg.admin')
1178 @HasPermissionAllDecorator('hg.admin')
1179 @CSRFRequired()
1179 @CSRFRequired()
1180 def groups_management_updates(self):
1180 def groups_management_updates(self):
1181 _ = self.request.translate
1181 _ = self.request.translate
1182 c = self.load_default_context()
1182 c = self.load_default_context()
1183
1183
1184 user_id = self.db_user_id
1184 user_id = self.db_user_id
1185 c.user = self.db_user
1185 c.user = self.db_user
1186
1186
1187 user_groups = set(self.request.POST.getall('users_group_id'))
1187 user_groups = set(self.request.POST.getall('users_group_id'))
1188 user_groups_objects = []
1188 user_groups_objects = []
1189
1189
1190 for ugid in user_groups:
1190 for ugid in user_groups:
1191 user_groups_objects.append(
1191 user_groups_objects.append(
1192 UserGroupModel().get_group(safe_int(ugid)))
1192 UserGroupModel().get_group(safe_int(ugid)))
1193 user_group_model = UserGroupModel()
1193 user_group_model = UserGroupModel()
1194 added_to_groups, removed_from_groups = \
1194 added_to_groups, removed_from_groups = \
1195 user_group_model.change_groups(c.user, user_groups_objects)
1195 user_group_model.change_groups(c.user, user_groups_objects)
1196
1196
1197 user_data = c.user.get_api_data()
1197 user_data = c.user.get_api_data()
1198 for user_group_id in added_to_groups:
1198 for user_group_id in added_to_groups:
1199 user_group = UserGroup.get(user_group_id)
1199 user_group = UserGroup.get(user_group_id)
1200 old_values = user_group.get_api_data()
1200 old_values = user_group.get_api_data()
1201 audit_logger.store_web(
1201 audit_logger.store_web(
1202 'user_group.edit.member.add',
1202 'user_group.edit.member.add',
1203 action_data={'user': user_data, 'old_data': old_values},
1203 action_data={'user': user_data, 'old_data': old_values},
1204 user=self._rhodecode_user)
1204 user=self._rhodecode_user)
1205
1205
1206 for user_group_id in removed_from_groups:
1206 for user_group_id in removed_from_groups:
1207 user_group = UserGroup.get(user_group_id)
1207 user_group = UserGroup.get(user_group_id)
1208 old_values = user_group.get_api_data()
1208 old_values = user_group.get_api_data()
1209 audit_logger.store_web(
1209 audit_logger.store_web(
1210 'user_group.edit.member.delete',
1210 'user_group.edit.member.delete',
1211 action_data={'user': user_data, 'old_data': old_values},
1211 action_data={'user': user_data, 'old_data': old_values},
1212 user=self._rhodecode_user)
1212 user=self._rhodecode_user)
1213
1213
1214 Session().commit()
1214 Session().commit()
1215 c.active = 'user_groups_management'
1215 c.active = 'user_groups_management'
1216 h.flash(_("Groups successfully changed"), category='success')
1216 h.flash(_("Groups successfully changed"), category='success')
1217
1217
1218 return HTTPFound(h.route_path(
1218 return HTTPFound(h.route_path(
1219 'edit_user_groups_management', user_id=user_id))
1219 'edit_user_groups_management', user_id=user_id))
1220
1220
1221 @LoginRequired()
1221 @LoginRequired()
1222 @HasPermissionAllDecorator('hg.admin')
1222 @HasPermissionAllDecorator('hg.admin')
1223 def user_audit_logs(self):
1223 def user_audit_logs(self):
1224 _ = self.request.translate
1224 _ = self.request.translate
1225 c = self.load_default_context()
1225 c = self.load_default_context()
1226 c.user = self.db_user
1226 c.user = self.db_user
1227
1227
1228 c.active = 'audit'
1228 c.active = 'audit'
1229
1229
1230 p = safe_int(self.request.GET.get('page', 1), 1)
1230 p = safe_int(self.request.GET.get('page', 1), 1)
1231
1231
1232 filter_term = self.request.GET.get('filter')
1232 filter_term = self.request.GET.get('filter')
1233 user_log = UserModel().get_user_log(c.user, filter_term)
1233 user_log = UserModel().get_user_log(c.user, filter_term)
1234
1234
1235 def url_generator(page_num):
1235 def url_generator(page_num):
1236 query_params = {
1236 query_params = {
1237 'page': page_num
1237 'page': page_num
1238 }
1238 }
1239 if filter_term:
1239 if filter_term:
1240 query_params['filter'] = filter_term
1240 query_params['filter'] = filter_term
1241 return self.request.current_route_path(_query=query_params)
1241 return self.request.current_route_path(_query=query_params)
1242
1242
1243 c.audit_logs = SqlPage(
1243 c.audit_logs = SqlPage(
1244 user_log, page=p, items_per_page=10, url_maker=url_generator)
1244 user_log, page=p, items_per_page=10, url_maker=url_generator)
1245 c.filter_term = filter_term
1245 c.filter_term = filter_term
1246 return self._get_template_context(c)
1246 return self._get_template_context(c)
1247
1247
1248 @LoginRequired()
1248 @LoginRequired()
1249 @HasPermissionAllDecorator('hg.admin')
1249 @HasPermissionAllDecorator('hg.admin')
1250 def user_audit_logs_download(self):
1250 def user_audit_logs_download(self):
1251 _ = self.request.translate
1251 _ = self.request.translate
1252 c = self.load_default_context()
1252 c = self.load_default_context()
1253 c.user = self.db_user
1253 c.user = self.db_user
1254
1254
1255 user_log = UserModel().get_user_log(c.user, filter_term=None)
1255 user_log = UserModel().get_user_log(c.user, filter_term=None)
1256
1256
1257 audit_log_data = {}
1257 audit_log_data = {}
1258 for entry in user_log:
1258 for entry in user_log:
1259 audit_log_data[entry.user_log_id] = entry.get_dict()
1259 audit_log_data[entry.user_log_id] = entry.get_dict()
1260
1260
1261 response = Response(ext_json.formatted_str_json(audit_log_data))
1261 response = Response(ext_json.formatted_str_json(audit_log_data))
1262 response.content_disposition = f'attachment; filename=user_{c.user.user_id}_audit_logs.json'
1262 response.content_disposition = f'attachment; filename=user_{c.user.user_id}_audit_logs.json'
1263 response.content_type = 'application/json'
1263 response.content_type = 'application/json'
1264
1264
1265 return response
1265 return response
1266
1266
1267 @LoginRequired()
1267 @LoginRequired()
1268 @HasPermissionAllDecorator('hg.admin')
1268 @HasPermissionAllDecorator('hg.admin')
1269 def user_perms_summary(self):
1269 def user_perms_summary(self):
1270 _ = self.request.translate
1270 _ = self.request.translate
1271 c = self.load_default_context()
1271 c = self.load_default_context()
1272 c.user = self.db_user
1272 c.user = self.db_user
1273
1273
1274 c.active = 'perms_summary'
1274 c.active = 'perms_summary'
1275 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1275 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1276
1276
1277 return self._get_template_context(c)
1277 return self._get_template_context(c)
1278
1278
1279 @LoginRequired()
1279 @LoginRequired()
1280 @HasPermissionAllDecorator('hg.admin')
1280 @HasPermissionAllDecorator('hg.admin')
1281 def user_perms_summary_json(self):
1281 def user_perms_summary_json(self):
1282 self.load_default_context()
1282 self.load_default_context()
1283 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1283 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1284
1284
1285 return perm_user.permissions
1285 return perm_user.permissions
1286
1286
1287 @LoginRequired()
1287 @LoginRequired()
1288 @HasPermissionAllDecorator('hg.admin')
1288 @HasPermissionAllDecorator('hg.admin')
1289 def user_caches(self):
1289 def user_caches(self):
1290 _ = self.request.translate
1290 _ = self.request.translate
1291 c = self.load_default_context()
1291 c = self.load_default_context()
1292 c.user = self.db_user
1292 c.user = self.db_user
1293
1293
1294 c.active = 'caches'
1294 c.active = 'caches'
1295 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1295 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1296
1296
1297 cache_namespace_uid = f'cache_user_auth.{rc_cache.PERMISSIONS_CACHE_VER}.{self.db_user.user_id}'
1297 cache_namespace_uid = f'cache_user_auth.{rc_cache.PERMISSIONS_CACHE_VER}.{self.db_user.user_id}'
1298 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1298 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1299 c.backend = c.region.backend
1299 c.backend = c.region.backend
1300 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1300 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1301
1301
1302 return self._get_template_context(c)
1302 return self._get_template_context(c)
1303
1303
1304 @LoginRequired()
1304 @LoginRequired()
1305 @HasPermissionAllDecorator('hg.admin')
1305 @HasPermissionAllDecorator('hg.admin')
1306 @CSRFRequired()
1306 @CSRFRequired()
1307 def user_caches_update(self):
1307 def user_caches_update(self):
1308 _ = self.request.translate
1308 _ = self.request.translate
1309 c = self.load_default_context()
1309 c = self.load_default_context()
1310 c.user = self.db_user
1310 c.user = self.db_user
1311
1311
1312 c.active = 'caches'
1312 c.active = 'caches'
1313 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1313 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1314
1314
1315 cache_namespace_uid = f'cache_user_auth.{rc_cache.PERMISSIONS_CACHE_VER}.{self.db_user.user_id}'
1315 cache_namespace_uid = f'cache_user_auth.{rc_cache.PERMISSIONS_CACHE_VER}.{self.db_user.user_id}'
1316 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid, method=rc_cache.CLEAR_DELETE)
1316 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid, method=rc_cache.CLEAR_DELETE)
1317
1317
1318 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1318 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1319
1319
1320 return HTTPFound(h.route_path(
1320 return HTTPFound(h.route_path(
1321 'edit_user_caches', user_id=c.user.user_id))
1321 'edit_user_caches', user_id=c.user.user_id))
@@ -1,144 +1,144 b''
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import logging
19 import logging
20
20
21 from pyramid.httpexceptions import HTTPFound
21 from pyramid.httpexceptions import HTTPFound
22
22
23 from rhodecode.apps._base import BaseAppView, DataGridAppView
23 from rhodecode.apps._base import BaseAppView, DataGridAppView
24 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
24 from rhodecode.apps.ssh_support.events import SshKeyFileChangeEvent
25 from rhodecode.events import trigger
25 from rhodecode.events import trigger
26 from rhodecode.lib import helpers as h
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib import audit_logger
27 from rhodecode.lib import audit_logger
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
29 from rhodecode.model.db import IntegrityError, UserSshKeys
29 from rhodecode.model.db import IntegrityError, UserSshKeys
30 from rhodecode.model.meta import Session
30 from rhodecode.model.meta import Session
31 from rhodecode.model.ssh_key import SshKeyModel
31 from rhodecode.model.ssh_key import SshKeyModel
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 class MyAccountSshKeysView(BaseAppView, DataGridAppView):
36 class MyAccountSshKeysView(BaseAppView, DataGridAppView):
37
37
38 def load_default_context(self):
38 def load_default_context(self):
39 c = self._get_local_tmpl_context()
39 c = self._get_local_tmpl_context()
40 c.user = c.auth_user.get_instance()
40 c.user = c.auth_user.get_instance()
41 c.ssh_enabled = self.request.registry.settings.get(
41 c.ssh_enabled = self.request.registry.settings.get(
42 'ssh.generate_authorized_keyfile')
42 'ssh.generate_authorized_keyfile')
43 return c
43 return c
44
44
45 @LoginRequired()
45 @LoginRequired()
46 @NotAnonymous()
46 @NotAnonymous()
47 def my_account_ssh_keys(self):
47 def my_account_ssh_keys(self):
48 _ = self.request.translate
48 _ = self.request.translate
49
49
50 c = self.load_default_context()
50 c = self.load_default_context()
51 c.active = 'ssh_keys'
51 c.active = 'ssh_keys'
52 c.default_key = self.request.GET.get('default_key')
52 c.default_key = self.request.GET.get('default_key')
53 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
53 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
54 return self._get_template_context(c)
54 return self._get_template_context(c)
55
55
56 @LoginRequired()
56 @LoginRequired()
57 @NotAnonymous()
57 @NotAnonymous()
58 def ssh_keys_generate_keypair(self):
58 def ssh_keys_generate_keypair(self):
59 _ = self.request.translate
59 _ = self.request.translate
60 c = self.load_default_context()
60 c = self.load_default_context()
61
61
62 c.active = 'ssh_keys_generate'
62 c.active = 'ssh_keys_generate'
63 if c.ssh_key_generator_enabled:
63 if c.ssh_key_generator_enabled:
64 private_format = self.request.GET.get('private_format') \
64 private_format = self.request.GET.get('private_format') \
65 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
65 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
66 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
66 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
67 c.private, c.public = SshKeyModel().generate_keypair(
67 c.private, c.public = SshKeyModel().generate_keypair(
68 comment=comment, private_format=private_format)
68 comment=comment, private_format=private_format)
69 c.target_form_url = h.route_path(
69 c.target_form_url = h.route_path(
70 'my_account_ssh_keys', _query=dict(default_key=c.public))
70 'my_account_ssh_keys', _query=dict(default_key=c.public))
71 return self._get_template_context(c)
71 return self._get_template_context(c)
72
72
73 @LoginRequired()
73 @LoginRequired()
74 @NotAnonymous()
74 @NotAnonymous()
75 @CSRFRequired()
75 @CSRFRequired()
76 def my_account_ssh_keys_add(self):
76 def my_account_ssh_keys_add(self):
77 _ = self.request.translate
77 _ = self.request.translate
78 c = self.load_default_context()
78 c = self.load_default_context()
79
79
80 user_data = c.user.get_api_data()
80 user_data = c.user.get_api_data()
81 key_data = self.request.POST.get('key_data')
81 key_data = self.request.POST.get('key_data')
82 description = self.request.POST.get('description')
82 description = self.request.POST.get('description')
83 fingerprint = 'unknown'
83 fingerprint = 'unknown'
84 try:
84 try:
85 if not key_data:
85 if not key_data:
86 raise ValueError('Please add a valid public key')
86 raise ValueError('Please add a valid public key')
87
87
88 key = SshKeyModel().parse_key(key_data.strip())
88 key = SshKeyModel().parse_key(key_data.strip())
89 fingerprint = key.hash_md5()
89 fingerprint = key.hash_md5()
90
90
91 ssh_key = SshKeyModel().create(
91 ssh_key = SshKeyModel().create(
92 c.user.user_id, fingerprint, key.keydata, description)
92 c.user.user_id, fingerprint, key.keydata, description)
93 ssh_key_data = ssh_key.get_api_data()
93 ssh_key_data = ssh_key.get_api_data()
94
94
95 audit_logger.store_web(
95 audit_logger.store_web(
96 'user.edit.ssh_key.add', action_data={
96 'user.edit.ssh_key.add', action_data={
97 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
97 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
98 user=self._rhodecode_user, )
98 user=self._rhodecode_user, )
99 Session().commit()
99 Session().commit()
100
100
101 # Trigger an event on change of keys.
101 # Trigger an event on change of keys.
102 trigger(SshKeyFileChangeEvent(), self.request.registry)
102 trigger(SshKeyFileChangeEvent(), self.request.registry)
103
103
104 h.flash(_("Ssh Key successfully created"), category='success')
104 h.flash(_("Ssh Key successfully created"), category='success')
105
105
106 except IntegrityError:
106 except IntegrityError:
107 log.exception("Exception during ssh key saving")
107 log.exception("Exception during ssh key saving")
108 err = 'Such key with fingerprint `{}` already exists, ' \
108 err = 'Such key with fingerprint `{}` already exists, ' \
109 'please use a different one'.format(fingerprint)
109 'please use a different one'.format(fingerprint)
110 h.flash(_('An error occurred during ssh key saving: {}').format(err),
110 h.flash(_('An error occurred during ssh key saving: {}').format(err),
111 category='error')
111 category='error')
112 except Exception as e:
112 except Exception as e:
113 log.exception("Exception during ssh key saving")
113 log.exception("Exception during ssh key saving")
114 h.flash(_('An error occurred during ssh key saving: {}').format(e),
114 h.flash(_('An error occurred during ssh key saving: {}').format(e),
115 category='error')
115 category='error')
116
116
117 return HTTPFound(h.route_path('my_account_ssh_keys'))
117 return HTTPFound(h.route_path('my_account_ssh_keys'))
118
118
119 @LoginRequired()
119 @LoginRequired()
120 @NotAnonymous()
120 @NotAnonymous()
121 @CSRFRequired()
121 @CSRFRequired()
122 def my_account_ssh_keys_delete(self):
122 def my_account_ssh_keys_delete(self):
123 _ = self.request.translate
123 _ = self.request.translate
124 c = self.load_default_context()
124 c = self.load_default_context()
125
125
126 user_data = c.user.get_api_data()
126 user_data = c.user.get_api_data()
127
127
128 del_ssh_key = self.request.POST.get('del_ssh_key')
128 del_ssh_key = self.request.POST.get('del_ssh_key')
129
129
130 if del_ssh_key:
130 if del_ssh_key:
131 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
131 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
132 ssh_key_data = ssh_key.get_api_data()
132 ssh_key_data = ssh_key.get_api_data()
133
133
134 SshKeyModel().delete(del_ssh_key, c.user.user_id)
134 SshKeyModel().delete(del_ssh_key, c.user.user_id)
135 audit_logger.store_web(
135 audit_logger.store_web(
136 'user.edit.ssh_key.delete', action_data={
136 'user.edit.ssh_key.delete', action_data={
137 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
137 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
138 user=self._rhodecode_user,)
138 user=self._rhodecode_user,)
139 Session().commit()
139 Session().commit()
140 # Trigger an event on change of keys.
140 # Trigger an event on change of keys.
141 trigger(SshKeyFileChangeEvent(), self.request.registry)
141 trigger(SshKeyFileChangeEvent(), self.request.registry)
142 h.flash(_("Ssh key successfully deleted"), category='success')
142 h.flash(_("Ssh key successfully deleted"), category='success')
143
143
144 return HTTPFound(h.route_path('my_account_ssh_keys'))
144 return HTTPFound(h.route_path('my_account_ssh_keys'))
@@ -1,59 +1,59 b''
1 # Copyright (C) 2016-2023 RhodeCode GmbH
1 # Copyright (C) 2016-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import logging
19 import logging
20
20
21 from . import config_keys
21 from . import config_keys
22 from .events import SshKeyFileChangeEvent
23 from .subscribers import generate_ssh_authorized_keys_file_subscriber
24
22
25 from rhodecode.config.settings_maker import SettingsMaker
23 from rhodecode.config.settings_maker import SettingsMaker
26
24
27 log = logging.getLogger(__name__)
25 log = logging.getLogger(__name__)
28
26
29
27
30 def _sanitize_settings_and_apply_defaults(settings):
28 def _sanitize_settings_and_apply_defaults(settings):
31 """
29 """
32 Set defaults, convert to python types and validate settings.
30 Set defaults, convert to python types and validate settings.
33 """
31 """
34 settings_maker = SettingsMaker(settings)
32 settings_maker = SettingsMaker(settings)
35
33
36 settings_maker.make_setting(config_keys.generate_authorized_keyfile, False, parser='bool')
34 settings_maker.make_setting(config_keys.generate_authorized_keyfile, False, parser='bool')
37 settings_maker.make_setting(config_keys.wrapper_allow_shell, False, parser='bool')
35 settings_maker.make_setting(config_keys.wrapper_allow_shell, False, parser='bool')
38 settings_maker.make_setting(config_keys.enable_debug_logging, False, parser='bool')
36 settings_maker.make_setting(config_keys.enable_debug_logging, False, parser='bool')
39 settings_maker.make_setting(config_keys.ssh_key_generator_enabled, True, parser='bool')
37 settings_maker.make_setting(config_keys.ssh_key_generator_enabled, True, parser='bool')
40
38
41 settings_maker.make_setting(config_keys.authorized_keys_file_path, '~/.ssh/authorized_keys_rhodecode')
39 settings_maker.make_setting(config_keys.authorized_keys_file_path, '~/.ssh/authorized_keys_rhodecode')
42 settings_maker.make_setting(config_keys.wrapper_cmd, '')
40 settings_maker.make_setting(config_keys.wrapper_cmd, '')
43 settings_maker.make_setting(config_keys.authorized_keys_line_ssh_opts, '')
41 settings_maker.make_setting(config_keys.authorized_keys_line_ssh_opts, '')
44
42
45 settings_maker.make_setting(config_keys.ssh_hg_bin, '/usr/local/bin/rhodecode_bin/vcs_bin/hg')
43 settings_maker.make_setting(config_keys.ssh_hg_bin, '/usr/local/bin/rhodecode_bin/vcs_bin/hg')
46 settings_maker.make_setting(config_keys.ssh_git_bin, '/usr/local/bin/rhodecode_bin/vcs_bin/git')
44 settings_maker.make_setting(config_keys.ssh_git_bin, '/usr/local/bin/rhodecode_bin/vcs_bin/git')
47 settings_maker.make_setting(config_keys.ssh_svn_bin, '/usr/local/bin/rhodecode_bin/vcs_bin/svnserve')
45 settings_maker.make_setting(config_keys.ssh_svn_bin, '/usr/local/bin/rhodecode_bin/vcs_bin/svnserve')
48
46
49 settings_maker.env_expand()
47 settings_maker.env_expand()
50
48
51
49
52 def includeme(config):
50 def includeme(config):
53 settings = config.registry.settings
51 settings = config.registry.settings
54 _sanitize_settings_and_apply_defaults(settings)
52 _sanitize_settings_and_apply_defaults(settings)
55
53
56 # if we have enable generation of file, subscribe to event
54 # if we have enable generation of file, subscribe to event
57 if settings[config_keys.generate_authorized_keyfile]:
55 if settings[config_keys.generate_authorized_keyfile]:
56 # lazy import here for faster code reading... via sshwrapper-v2 mode
57 from .subscribers import generate_ssh_authorized_keys_file_subscriber
58 config.add_subscriber(
58 config.add_subscriber(
59 generate_ssh_authorized_keys_file_subscriber, SshKeyFileChangeEvent)
59 generate_ssh_authorized_keys_file_subscriber, SshKeyFileChangeEvent)
General Comments 0
You need to be logged in to leave comments. Login now