##// END OF EJS Templates
admin: cleanup imports.
marcink -
r2079:9c79ce85 default
parent child Browse files
Show More
@@ -1,73 +1,72 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from pyramid.view import view_config
24 from sqlalchemy.orm import joinedload
25 24
26 25 from rhodecode.apps._base import BaseAppView
27 from rhodecode.model.db import UserLog
26 from rhodecode.model.db import joinedload, UserLog
28 27 from rhodecode.lib.user_log_filter import user_log_filter
29 28 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
30 29 from rhodecode.lib.utils2 import safe_int
31 30 from rhodecode.lib.helpers import Page
32 31
33 32 log = logging.getLogger(__name__)
34 33
35 34
36 35 class AdminAuditLogsView(BaseAppView):
37 36 def load_default_context(self):
38 37 c = self._get_local_tmpl_context()
39 38 self._register_global_c(c)
40 39 return c
41 40
42 41 @LoginRequired()
43 42 @HasPermissionAllDecorator('hg.admin')
44 43 @view_config(
45 44 route_name='admin_audit_logs', request_method='GET',
46 45 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
47 46 def admin_audit_logs(self):
48 47 c = self.load_default_context()
49 48
50 49 users_log = UserLog.query()\
51 50 .options(joinedload(UserLog.user))\
52 51 .options(joinedload(UserLog.repository))
53 52
54 53 # FILTERING
55 54 c.search_term = self.request.GET.get('filter')
56 55 try:
57 56 users_log = user_log_filter(users_log, c.search_term)
58 57 except Exception:
59 58 # we want this to crash for now
60 59 raise
61 60
62 61 users_log = users_log.order_by(UserLog.action_date.desc())
63 62
64 63 p = safe_int(self.request.GET.get('page', 1), 1)
65 64
66 65 def url_generator(**kw):
67 66 if c.search_term:
68 67 kw['filter'] = c.search_term
69 68 return self.request.current_route_path(_query=kw)
70 69
71 70 c.audit_logs = Page(users_log, page=p, items_per_page=10,
72 71 url=url_generator)
73 72 return self._get_template_context(c)
@@ -1,65 +1,64 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23
24 23 from pyramid.httpexceptions import HTTPFound
25 24 from pyramid.view import view_config
26 25
27 26 from rhodecode.apps._base import BaseAppView
28 27 from rhodecode.lib import helpers as h
29 28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 29 from rhodecode.model.db import PullRequest
31 30
32 31
33 32 log = logging.getLogger(__name__)
34 33
35 34
36 35 class AdminMainView(BaseAppView):
37 36
38 37 @LoginRequired()
39 38 @HasPermissionAllDecorator('hg.admin')
40 39 @view_config(
41 40 route_name='admin_home', request_method='GET')
42 41 def admin_main(self):
43 42 # redirect _admin to audit logs...
44 43 raise HTTPFound(h.route_path('admin_audit_logs'))
45 44
46 45 @LoginRequired()
47 46 @view_config(route_name='pull_requests_global_0', request_method='GET')
48 47 @view_config(route_name='pull_requests_global_1', request_method='GET')
49 48 @view_config(route_name='pull_requests_global', request_method='GET')
50 49 def pull_requests(self):
51 50 """
52 51 Global redirect for Pull Requests
53 52
54 53 :param pull_request_id: id of pull requests in the system
55 54 """
56 55
57 56 pull_request = PullRequest.get_or_404(
58 57 self.request.matchdict['pull_request_id'])
59 58 pull_request_id = pull_request.pull_request_id
60 59
61 60 repo_name = pull_request.target_repo.repo_name
62 61
63 62 raise HTTPFound(
64 63 h.route_path('pullrequest_show', repo_name=repo_name,
65 64 pull_request_id=pull_request_id))
@@ -1,54 +1,53 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import collections
22 22 import logging
23 23
24
25 24 from pyramid.view import view_config
26 25
27 26 from rhodecode.apps._base import BaseAppView
28 27 from rhodecode.apps.admin.navigation import navigation_list
29 28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 29 from rhodecode.lib.utils import read_opensource_licenses
31 30
32 31 log = logging.getLogger(__name__)
33 32
34 33
35 34 class OpenSourceLicensesAdminSettingsView(BaseAppView):
36 35
37 36 def load_default_context(self):
38 37 c = self._get_local_tmpl_context()
39 38 self._register_global_c(c)
40 39 return c
41 40
42 41 @LoginRequired()
43 42 @HasPermissionAllDecorator('hg.admin')
44 43 @view_config(
45 44 route_name='admin_settings_open_source', request_method='GET',
46 45 renderer='rhodecode:templates/admin/settings/settings.mako')
47 46 def open_source_licenses(self):
48 47 c = self.load_default_context()
49 48 c.active = 'open_source'
50 49 c.navlist = navigation_list(self.request)
51 50 items = sorted(read_opensource_licenses().items(), key=lambda t: t[0])
52 51 c.opensource_licenses = collections.OrderedDict(items)
53 52
54 53 return self._get_template_context(c)
@@ -1,479 +1,481 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import re
22 22 import logging
23 23 import formencode
24 import formencode.htmlfill
24 25 import datetime
25 26 from pyramid.interfaces import IRoutesMapper
26 27
27 28 from pyramid.view import view_config
28 29 from pyramid.httpexceptions import HTTPFound
29 30 from pyramid.renderers import render
30 31 from pyramid.response import Response
31 32
32 33 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 34 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 35 from rhodecode.events import trigger
35 36
36 37 from rhodecode.lib import helpers as h
37 38 from rhodecode.lib.auth import (
38 39 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
39 40 from rhodecode.lib.utils2 import aslist, safe_unicode
40 from rhodecode.model.db import or_, joinedload, coalesce, User, UserIpMap, UserSshKeys
41 from rhodecode.model.db import (
42 or_, coalesce, User, UserIpMap, UserSshKeys)
41 43 from rhodecode.model.forms import (
42 44 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
43 45 from rhodecode.model.meta import Session
44 46 from rhodecode.model.permission import PermissionModel
45 47 from rhodecode.model.settings import SettingsModel
46 48
47 49
48 50 log = logging.getLogger(__name__)
49 51
50 52
51 53 class AdminPermissionsView(BaseAppView, DataGridAppView):
52 54 def load_default_context(self):
53 55 c = self._get_local_tmpl_context()
54 56
55 57 self._register_global_c(c)
56 58 PermissionModel().set_global_permission_choices(
57 59 c, gettext_translator=self.request.translate)
58 60 return c
59 61
60 62 @LoginRequired()
61 63 @HasPermissionAllDecorator('hg.admin')
62 64 @view_config(
63 65 route_name='admin_permissions_application', request_method='GET',
64 66 renderer='rhodecode:templates/admin/permissions/permissions.mako')
65 67 def permissions_application(self):
66 68 c = self.load_default_context()
67 69 c.active = 'application'
68 70
69 71 c.user = User.get_default_user(refresh=True)
70 72
71 73 app_settings = SettingsModel().get_all_settings()
72 74 defaults = {
73 75 'anonymous': c.user.active,
74 76 'default_register_message': app_settings.get(
75 77 'rhodecode_register_message')
76 78 }
77 79 defaults.update(c.user.get_default_perms())
78 80
79 81 data = render('rhodecode:templates/admin/permissions/permissions.mako',
80 82 self._get_template_context(c), self.request)
81 83 html = formencode.htmlfill.render(
82 84 data,
83 85 defaults=defaults,
84 86 encoding="UTF-8",
85 87 force_defaults=False
86 88 )
87 89 return Response(html)
88 90
89 91 @LoginRequired()
90 92 @HasPermissionAllDecorator('hg.admin')
91 93 @CSRFRequired()
92 94 @view_config(
93 95 route_name='admin_permissions_application_update', request_method='POST',
94 96 renderer='rhodecode:templates/admin/permissions/permissions.mako')
95 97 def permissions_application_update(self):
96 98 _ = self.request.translate
97 99 c = self.load_default_context()
98 100 c.active = 'application'
99 101
100 102 _form = ApplicationPermissionsForm(
101 103 [x[0] for x in c.register_choices],
102 104 [x[0] for x in c.password_reset_choices],
103 105 [x[0] for x in c.extern_activate_choices])()
104 106
105 107 try:
106 108 form_result = _form.to_python(dict(self.request.POST))
107 109 form_result.update({'perm_user_name': User.DEFAULT_USER})
108 110 PermissionModel().update_application_permissions(form_result)
109 111
110 112 settings = [
111 113 ('register_message', 'default_register_message'),
112 114 ]
113 115 for setting, form_key in settings:
114 116 sett = SettingsModel().create_or_update_setting(
115 117 setting, form_result[form_key])
116 118 Session().add(sett)
117 119
118 120 Session().commit()
119 121 h.flash(_('Application permissions updated successfully'),
120 122 category='success')
121 123
122 124 except formencode.Invalid as errors:
123 125 defaults = errors.value
124 126
125 127 data = render(
126 128 'rhodecode:templates/admin/permissions/permissions.mako',
127 129 self._get_template_context(c), self.request)
128 130 html = formencode.htmlfill.render(
129 131 data,
130 132 defaults=defaults,
131 133 errors=errors.error_dict or {},
132 134 prefix_error=False,
133 135 encoding="UTF-8",
134 136 force_defaults=False
135 137 )
136 138 return Response(html)
137 139
138 140 except Exception:
139 141 log.exception("Exception during update of permissions")
140 142 h.flash(_('Error occurred during update of permissions'),
141 143 category='error')
142 144
143 145 raise HTTPFound(h.route_path('admin_permissions_application'))
144 146
145 147 @LoginRequired()
146 148 @HasPermissionAllDecorator('hg.admin')
147 149 @view_config(
148 150 route_name='admin_permissions_object', request_method='GET',
149 151 renderer='rhodecode:templates/admin/permissions/permissions.mako')
150 152 def permissions_objects(self):
151 153 c = self.load_default_context()
152 154 c.active = 'objects'
153 155
154 156 c.user = User.get_default_user(refresh=True)
155 157 defaults = {}
156 158 defaults.update(c.user.get_default_perms())
157 159
158 160 data = render(
159 161 'rhodecode:templates/admin/permissions/permissions.mako',
160 162 self._get_template_context(c), self.request)
161 163 html = formencode.htmlfill.render(
162 164 data,
163 165 defaults=defaults,
164 166 encoding="UTF-8",
165 167 force_defaults=False
166 168 )
167 169 return Response(html)
168 170
169 171 @LoginRequired()
170 172 @HasPermissionAllDecorator('hg.admin')
171 173 @CSRFRequired()
172 174 @view_config(
173 175 route_name='admin_permissions_object_update', request_method='POST',
174 176 renderer='rhodecode:templates/admin/permissions/permissions.mako')
175 177 def permissions_objects_update(self):
176 178 _ = self.request.translate
177 179 c = self.load_default_context()
178 180 c.active = 'objects'
179 181
180 182 _form = ObjectPermissionsForm(
181 183 [x[0] for x in c.repo_perms_choices],
182 184 [x[0] for x in c.group_perms_choices],
183 185 [x[0] for x in c.user_group_perms_choices])()
184 186
185 187 try:
186 188 form_result = _form.to_python(dict(self.request.POST))
187 189 form_result.update({'perm_user_name': User.DEFAULT_USER})
188 190 PermissionModel().update_object_permissions(form_result)
189 191
190 192 Session().commit()
191 193 h.flash(_('Object permissions updated successfully'),
192 194 category='success')
193 195
194 196 except formencode.Invalid as errors:
195 197 defaults = errors.value
196 198
197 199 data = render(
198 200 'rhodecode:templates/admin/permissions/permissions.mako',
199 201 self._get_template_context(c), self.request)
200 202 html = formencode.htmlfill.render(
201 203 data,
202 204 defaults=defaults,
203 205 errors=errors.error_dict or {},
204 206 prefix_error=False,
205 207 encoding="UTF-8",
206 208 force_defaults=False
207 209 )
208 210 return Response(html)
209 211 except Exception:
210 212 log.exception("Exception during update of permissions")
211 213 h.flash(_('Error occurred during update of permissions'),
212 214 category='error')
213 215
214 216 raise HTTPFound(h.route_path('admin_permissions_object'))
215 217
216 218 @LoginRequired()
217 219 @HasPermissionAllDecorator('hg.admin')
218 220 @view_config(
219 221 route_name='admin_permissions_global', request_method='GET',
220 222 renderer='rhodecode:templates/admin/permissions/permissions.mako')
221 223 def permissions_global(self):
222 224 c = self.load_default_context()
223 225 c.active = 'global'
224 226
225 227 c.user = User.get_default_user(refresh=True)
226 228 defaults = {}
227 229 defaults.update(c.user.get_default_perms())
228 230
229 231 data = render(
230 232 'rhodecode:templates/admin/permissions/permissions.mako',
231 233 self._get_template_context(c), self.request)
232 234 html = formencode.htmlfill.render(
233 235 data,
234 236 defaults=defaults,
235 237 encoding="UTF-8",
236 238 force_defaults=False
237 239 )
238 240 return Response(html)
239 241
240 242 @LoginRequired()
241 243 @HasPermissionAllDecorator('hg.admin')
242 244 @CSRFRequired()
243 245 @view_config(
244 246 route_name='admin_permissions_global_update', request_method='POST',
245 247 renderer='rhodecode:templates/admin/permissions/permissions.mako')
246 248 def permissions_global_update(self):
247 249 _ = self.request.translate
248 250 c = self.load_default_context()
249 251 c.active = 'global'
250 252
251 253 _form = UserPermissionsForm(
252 254 [x[0] for x in c.repo_create_choices],
253 255 [x[0] for x in c.repo_create_on_write_choices],
254 256 [x[0] for x in c.repo_group_create_choices],
255 257 [x[0] for x in c.user_group_create_choices],
256 258 [x[0] for x in c.fork_choices],
257 259 [x[0] for x in c.inherit_default_permission_choices])()
258 260
259 261 try:
260 262 form_result = _form.to_python(dict(self.request.POST))
261 263 form_result.update({'perm_user_name': User.DEFAULT_USER})
262 264 PermissionModel().update_user_permissions(form_result)
263 265
264 266 Session().commit()
265 267 h.flash(_('Global permissions updated successfully'),
266 268 category='success')
267 269
268 270 except formencode.Invalid as errors:
269 271 defaults = errors.value
270 272
271 273 data = render(
272 274 'rhodecode:templates/admin/permissions/permissions.mako',
273 275 self._get_template_context(c), self.request)
274 276 html = formencode.htmlfill.render(
275 277 data,
276 278 defaults=defaults,
277 279 errors=errors.error_dict or {},
278 280 prefix_error=False,
279 281 encoding="UTF-8",
280 282 force_defaults=False
281 283 )
282 284 return Response(html)
283 285 except Exception:
284 286 log.exception("Exception during update of permissions")
285 287 h.flash(_('Error occurred during update of permissions'),
286 288 category='error')
287 289
288 290 raise HTTPFound(h.route_path('admin_permissions_global'))
289 291
290 292 @LoginRequired()
291 293 @HasPermissionAllDecorator('hg.admin')
292 294 @view_config(
293 295 route_name='admin_permissions_ips', request_method='GET',
294 296 renderer='rhodecode:templates/admin/permissions/permissions.mako')
295 297 def permissions_ips(self):
296 298 c = self.load_default_context()
297 299 c.active = 'ips'
298 300
299 301 c.user = User.get_default_user(refresh=True)
300 302 c.user_ip_map = (
301 303 UserIpMap.query().filter(UserIpMap.user == c.user).all())
302 304
303 305 return self._get_template_context(c)
304 306
305 307 @LoginRequired()
306 308 @HasPermissionAllDecorator('hg.admin')
307 309 @view_config(
308 310 route_name='admin_permissions_overview', request_method='GET',
309 311 renderer='rhodecode:templates/admin/permissions/permissions.mako')
310 312 def permissions_overview(self):
311 313 c = self.load_default_context()
312 314 c.active = 'perms'
313 315
314 316 c.user = User.get_default_user(refresh=True)
315 317 c.perm_user = c.user.AuthUser()
316 318 return self._get_template_context(c)
317 319
318 320 @LoginRequired()
319 321 @HasPermissionAllDecorator('hg.admin')
320 322 @view_config(
321 323 route_name='admin_permissions_auth_token_access', request_method='GET',
322 324 renderer='rhodecode:templates/admin/permissions/permissions.mako')
323 325 def auth_token_access(self):
324 326 from rhodecode import CONFIG
325 327
326 328 c = self.load_default_context()
327 329 c.active = 'auth_token_access'
328 330
329 331 c.user = User.get_default_user(refresh=True)
330 332 c.perm_user = c.user.AuthUser()
331 333
332 334 mapper = self.request.registry.queryUtility(IRoutesMapper)
333 335 c.view_data = []
334 336
335 337 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
336 338 introspector = self.request.registry.introspector
337 339
338 340 view_intr = {}
339 341 for view_data in introspector.get_category('views'):
340 342 intr = view_data['introspectable']
341 343
342 344 if 'route_name' in intr and intr['attr']:
343 345 view_intr[intr['route_name']] = '{}:{}'.format(
344 346 str(intr['derived_callable'].func_name), intr['attr']
345 347 )
346 348
347 349 c.whitelist_key = 'api_access_controllers_whitelist'
348 350 c.whitelist_file = CONFIG.get('__file__')
349 351 whitelist_views = aslist(
350 352 CONFIG.get(c.whitelist_key), sep=',')
351 353
352 354 for route_info in mapper.get_routes():
353 355 if not route_info.name.startswith('__'):
354 356 routepath = route_info.pattern
355 357
356 358 def replace(matchobj):
357 359 if matchobj.group(1):
358 360 return "{%s}" % matchobj.group(1).split(':')[0]
359 361 else:
360 362 return "{%s}" % matchobj.group(2)
361 363
362 364 routepath = _argument_prog.sub(replace, routepath)
363 365
364 366 if not routepath.startswith('/'):
365 367 routepath = '/' + routepath
366 368
367 369 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
368 370 active = view_fqn in whitelist_views
369 371 c.view_data.append((route_info.name, view_fqn, routepath, active))
370 372
371 373 c.whitelist_views = whitelist_views
372 374 return self._get_template_context(c)
373 375
374 376 def ssh_enabled(self):
375 377 return self.request.registry.settings.get(
376 378 'ssh.generate_authorized_keyfile')
377 379
378 380 @LoginRequired()
379 381 @HasPermissionAllDecorator('hg.admin')
380 382 @view_config(
381 383 route_name='admin_permissions_ssh_keys', request_method='GET',
382 384 renderer='rhodecode:templates/admin/permissions/permissions.mako')
383 385 def ssh_keys(self):
384 386 c = self.load_default_context()
385 387 c.active = 'ssh_keys'
386 388 c.ssh_enabled = self.ssh_enabled()
387 389 return self._get_template_context(c)
388 390
389 391 @LoginRequired()
390 392 @HasPermissionAllDecorator('hg.admin')
391 393 @view_config(
392 394 route_name='admin_permissions_ssh_keys_data', request_method='GET',
393 395 renderer='json_ext', xhr=True)
394 396 def ssh_keys_data(self):
395 397 _ = self.request.translate
396 398 column_map = {
397 399 'fingerprint': 'ssh_key_fingerprint',
398 400 'username': User.username
399 401 }
400 402 draw, start, limit = self._extract_chunk(self.request)
401 403 search_q, order_by, order_dir = self._extract_ordering(
402 404 self.request, column_map=column_map)
403 405
404 406 ssh_keys_data_total_count = UserSshKeys.query()\
405 407 .count()
406 408
407 409 # json generate
408 410 base_q = UserSshKeys.query().join(UserSshKeys.user)
409 411
410 412 if search_q:
411 413 like_expression = u'%{}%'.format(safe_unicode(search_q))
412 414 base_q = base_q.filter(or_(
413 415 User.username.ilike(like_expression),
414 416 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
415 417 ))
416 418
417 419 users_data_total_filtered_count = base_q.count()
418 420
419 421 sort_col = self._get_order_col(order_by, UserSshKeys)
420 422 if sort_col:
421 423 if order_dir == 'asc':
422 424 # handle null values properly to order by NULL last
423 425 if order_by in ['created_on']:
424 426 sort_col = coalesce(sort_col, datetime.date.max)
425 427 sort_col = sort_col.asc()
426 428 else:
427 429 # handle null values properly to order by NULL last
428 430 if order_by in ['created_on']:
429 431 sort_col = coalesce(sort_col, datetime.date.min)
430 432 sort_col = sort_col.desc()
431 433
432 434 base_q = base_q.order_by(sort_col)
433 435 base_q = base_q.offset(start).limit(limit)
434 436
435 437 ssh_keys = base_q.all()
436 438
437 439 ssh_keys_data = []
438 440 for ssh_key in ssh_keys:
439 441 ssh_keys_data.append({
440 442 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
441 443 "fingerprint": ssh_key.ssh_key_fingerprint,
442 444 "description": ssh_key.description,
443 445 "created_on": h.format_date(ssh_key.created_on),
444 446 "action": h.link_to(
445 447 _('Edit'), h.route_path('edit_user_ssh_keys',
446 448 user_id=ssh_key.user.user_id))
447 449 })
448 450
449 451 data = ({
450 452 'draw': draw,
451 453 'data': ssh_keys_data,
452 454 'recordsTotal': ssh_keys_data_total_count,
453 455 'recordsFiltered': users_data_total_filtered_count,
454 456 })
455 457
456 458 return data
457 459
458 460 @LoginRequired()
459 461 @HasPermissionAllDecorator('hg.admin')
460 462 @CSRFRequired()
461 463 @view_config(
462 464 route_name='admin_permissions_ssh_keys_update', request_method='POST',
463 465 renderer='rhodecode:templates/admin/permissions/permissions.mako')
464 466 def ssh_keys_update(self):
465 467 _ = self.request.translate
466 468 self.load_default_context()
467 469
468 470 ssh_enabled = self.ssh_enabled()
469 471 key_file = self.request.registry.settings.get(
470 472 'ssh.authorized_keys_file_path')
471 473 if ssh_enabled:
472 474 trigger(SshKeyFileChangeEvent(), self.request.registry)
473 475 h.flash(_('Updated SSH keys file: {}').format(key_file),
474 476 category='success')
475 477 else:
476 478 h.flash(_('SSH key support is disabled in .ini file'),
477 479 category='warning')
478 480
479 481 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -1,92 +1,91 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 import psutil
24 24 from pyramid.view import view_config
25 25
26 26 from rhodecode.apps._base import BaseAppView
27 27 from rhodecode.apps.admin.navigation import navigation_list
28 from rhodecode.lib import helpers as h
29 28 from rhodecode.lib.auth import (
30 29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 30 from rhodecode.lib.utils2 import safe_int
32 31
33 32 log = logging.getLogger(__name__)
34 33
35 34
36 35 class AdminProcessManagementView(BaseAppView):
37 36 def load_default_context(self):
38 37 c = self._get_local_tmpl_context()
39 38 self._register_global_c(c)
40 39 return c
41 40
42 41 @LoginRequired()
43 42 @HasPermissionAllDecorator('hg.admin')
44 43 @view_config(
45 44 route_name='admin_settings_process_management', request_method='GET',
46 45 renderer='rhodecode:templates/admin/settings/settings.mako')
47 46 def process_management(self):
48 47 _ = self.request.translate
49 48 c = self.load_default_context()
50 49
51 50 c.active = 'process_management'
52 51 c.navlist = navigation_list(self.request)
53 52 c.gunicorn_processes = (
54 53 p for p in psutil.process_iter() if 'gunicorn' in p.name())
55 54 return self._get_template_context(c)
56 55
57 56 @LoginRequired()
58 57 @HasPermissionAllDecorator('hg.admin')
59 58 @CSRFRequired()
60 59 @view_config(
61 60 route_name='admin_settings_process_management_signal',
62 61 request_method='POST', renderer='json_ext')
63 62 def process_management_signal(self):
64 63 pids = self.request.json.get('pids', [])
65 64 result = []
66 65 def on_terminate(proc):
67 66 msg = "process `PID:{}` terminated with exit code {}".format(
68 67 proc.pid, proc.returncode)
69 68 result.append(msg)
70 69
71 70 procs = []
72 71 for pid in pids:
73 72 pid = safe_int(pid)
74 73 if pid:
75 74 try:
76 75 proc = psutil.Process(pid)
77 76 except psutil.NoSuchProcess:
78 77 continue
79 78
80 79 children = proc.children(recursive=True)
81 80 if children:
82 81 print('Wont kill Master Process')
83 82 else:
84 83 procs.append(proc)
85 84
86 85 for p in procs:
87 86 p.terminate()
88 87 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
89 88 for p in alive:
90 89 p.kill()
91 90
92 91 return {'result': result}
@@ -1,180 +1,181 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import formencode
23 import formencode.htmlfill
23 24
24 25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 26 from pyramid.view import view_config
26 27 from pyramid.renderers import render
27 28 from pyramid.response import Response
28 29
29 30 from rhodecode.apps._base import BaseAppView, DataGridAppView
30 31
31 32 from rhodecode.lib.ext_json import json
32 33 from rhodecode.lib.auth import (
33 34 LoginRequired, CSRFRequired, NotAnonymous,
34 35 HasPermissionAny, HasRepoGroupPermissionAny)
35 36 from rhodecode.lib import helpers as h
36 37 from rhodecode.lib.utils import repo_name_slug
37 38 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 39 from rhodecode.model.forms import RepoForm
39 40 from rhodecode.model.repo import RepoModel
40 41 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
41 42 from rhodecode.model.settings import SettingsModel
42 43 from rhodecode.model.db import Repository, RepoGroup
43 44
44 45 log = logging.getLogger(__name__)
45 46
46 47
47 48 class AdminReposView(BaseAppView, DataGridAppView):
48 49
49 50 def load_default_context(self):
50 51 c = self._get_local_tmpl_context()
51 52 self._register_global_c(c)
52 53 return c
53 54
54 55 def _load_form_data(self, c):
55 56 acl_groups = RepoGroupList(RepoGroup.query().all(),
56 57 perm_set=['group.write', 'group.admin'])
57 58 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
58 59 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
59 60 c.landing_revs_choices, c.landing_revs = \
60 61 ScmModel().get_repo_landing_revs()
61 62 c.personal_repo_group = self._rhodecode_user.personal_repo_group
62 63
63 64 @LoginRequired()
64 65 @NotAnonymous()
65 66 @view_config(
66 67 route_name='repos', request_method='GET',
67 68 renderer='rhodecode:templates/admin/repos/repos.mako')
68 69 def repository_list(self):
69 70 c = self.load_default_context()
70 71
71 72 repo_list = Repository.get_all_repos()
72 73 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
73 74 repos_data = RepoModel().get_repos_as_dict(
74 75 repo_list=c.repo_list, admin=True, super_user_actions=True)
75 76 # json used to render the grid
76 77 c.data = json.dumps(repos_data)
77 78
78 79 return self._get_template_context(c)
79 80
80 81 @LoginRequired()
81 82 @NotAnonymous()
82 83 # perms check inside
83 84 @view_config(
84 85 route_name='repo_new', request_method='GET',
85 86 renderer='rhodecode:templates/admin/repos/repo_add.mako')
86 87 def repository_new(self):
87 88 c = self.load_default_context()
88 89
89 90 new_repo = self.request.GET.get('repo', '')
90 91 parent_group = safe_int(self.request.GET.get('parent_group'))
91 92 _gr = RepoGroup.get(parent_group)
92 93
93 94 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
94 95 # you're not super admin nor have global create permissions,
95 96 # but maybe you have at least write permission to a parent group ?
96 97
97 98 gr_name = _gr.group_name if _gr else None
98 99 # create repositories with write permission on group is set to true
99 100 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
100 101 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
101 102 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
102 103 if not (group_admin or (group_write and create_on_write)):
103 104 raise HTTPForbidden()
104 105
105 106 self._load_form_data(c)
106 107 c.new_repo = repo_name_slug(new_repo)
107 108
108 109 # apply the defaults from defaults page
109 110 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
110 111 # set checkbox to autochecked
111 112 defaults['repo_copy_permissions'] = True
112 113
113 114 parent_group_choice = '-1'
114 115 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
115 116 parent_group_choice = self._rhodecode_user.personal_repo_group
116 117
117 118 if parent_group and _gr:
118 119 if parent_group in [x[0] for x in c.repo_groups]:
119 120 parent_group_choice = safe_unicode(parent_group)
120 121
121 122 defaults.update({'repo_group': parent_group_choice})
122 123
123 124 data = render('rhodecode:templates/admin/repos/repo_add.mako',
124 125 self._get_template_context(c), self.request)
125 126 html = formencode.htmlfill.render(
126 127 data,
127 128 defaults=defaults,
128 129 encoding="UTF-8",
129 130 force_defaults=False
130 131 )
131 132 return Response(html)
132 133
133 134 @LoginRequired()
134 135 @NotAnonymous()
135 136 @CSRFRequired()
136 137 # perms check inside
137 138 @view_config(
138 139 route_name='repo_create', request_method='POST',
139 140 renderer='rhodecode:templates/admin/repos/repos.mako')
140 141 def repository_create(self):
141 142 c = self.load_default_context()
142 143
143 144 form_result = {}
144 145 task_id = None
145 146 self._load_form_data(c)
146 147
147 148 try:
148 149 # CanWriteToGroup validators checks permissions of this POST
149 150 form_result = RepoForm(repo_groups=c.repo_groups_choices,
150 151 landing_revs=c.landing_revs_choices)()\
151 152 .to_python(dict(self.request.POST))
152 153
153 154 # create is done sometimes async on celery, db transaction
154 155 # management is handled there.
155 156 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
156 157 from celery.result import BaseAsyncResult
157 158 if isinstance(task, BaseAsyncResult):
158 159 task_id = task.task_id
159 160 except formencode.Invalid as errors:
160 161 data = render('rhodecode:templates/admin/repos/repo_add.mako',
161 162 self._get_template_context(c), self.request)
162 163 html = formencode.htmlfill.render(
163 164 data,
164 165 defaults=errors.value,
165 166 errors=errors.error_dict or {},
166 167 prefix_error=False,
167 168 encoding="UTF-8",
168 169 force_defaults=False
169 170 )
170 171 return Response(html)
171 172
172 173 except Exception as e:
173 174 msg = self._log_creation_exception(e, form_result.get('repo_name'))
174 175 h.flash(msg, category='error')
175 176 raise HTTPFound(h.route_path('home'))
176 177
177 178 raise HTTPFound(
178 179 h.route_path('repo_creating',
179 180 repo_name=form_result['repo_name_full'],
180 181 _query=dict(task_id=task_id)))
@@ -1,673 +1,674 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23 import formencode
24 import formencode.htmlfill
24 25
25 26 from pyramid.httpexceptions import HTTPFound
26 27 from pyramid.view import view_config
27 28 from sqlalchemy.sql.functions import coalesce
28 29 from sqlalchemy.exc import IntegrityError
29 30
30 31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 33 from rhodecode.events import trigger
33 34
34 35 from rhodecode.lib import audit_logger
35 36 from rhodecode.lib.ext_json import json
36 37 from rhodecode.lib.auth import (
37 38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
38 39 from rhodecode.lib import helpers as h
39 40 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 41 from rhodecode.model.auth_token import AuthTokenModel
41 42 from rhodecode.model.ssh_key import SshKeyModel
42 43 from rhodecode.model.user import UserModel
43 44 from rhodecode.model.user_group import UserGroupModel
44 45 from rhodecode.model.db import (
45 46 or_, User, UserIpMap, UserEmailMap, UserApiKeys, UserSshKeys)
46 47 from rhodecode.model.meta import Session
47 48
48 49 log = logging.getLogger(__name__)
49 50
50 51
51 52 class AdminUsersView(BaseAppView, DataGridAppView):
52 53 ALLOW_SCOPED_TOKENS = False
53 54 """
54 55 This view has alternative version inside EE, if modified please take a look
55 56 in there as well.
56 57 """
57 58
58 59 def load_default_context(self):
59 60 c = self._get_local_tmpl_context()
60 61 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
61 62 self._register_global_c(c)
62 63 return c
63 64
64 65 def _redirect_for_default_user(self, username):
65 66 _ = self.request.translate
66 67 if username == User.DEFAULT_USER:
67 68 h.flash(_("You can't edit this user"), category='warning')
68 69 # TODO(marcink): redirect to 'users' admin panel once this
69 70 # is a pyramid view
70 71 raise HTTPFound('/')
71 72
72 73 @LoginRequired()
73 74 @HasPermissionAllDecorator('hg.admin')
74 75 @view_config(
75 76 route_name='users', request_method='GET',
76 77 renderer='rhodecode:templates/admin/users/users.mako')
77 78 def users_list(self):
78 79 c = self.load_default_context()
79 80 return self._get_template_context(c)
80 81
81 82 @LoginRequired()
82 83 @HasPermissionAllDecorator('hg.admin')
83 84 @view_config(
84 85 # renderer defined below
85 86 route_name='users_data', request_method='GET',
86 87 renderer='json_ext', xhr=True)
87 88 def users_list_data(self):
88 89 column_map = {
89 90 'first_name': 'name',
90 91 'last_name': 'lastname',
91 92 }
92 93 draw, start, limit = self._extract_chunk(self.request)
93 94 search_q, order_by, order_dir = self._extract_ordering(
94 95 self.request, column_map=column_map)
95 96
96 97 _render = self.request.get_partial_renderer(
97 98 'data_table/_dt_elements.mako')
98 99
99 100 def user_actions(user_id, username):
100 101 return _render("user_actions", user_id, username)
101 102
102 103 users_data_total_count = User.query()\
103 104 .filter(User.username != User.DEFAULT_USER) \
104 105 .count()
105 106
106 107 # json generate
107 108 base_q = User.query().filter(User.username != User.DEFAULT_USER)
108 109
109 110 if search_q:
110 111 like_expression = u'%{}%'.format(safe_unicode(search_q))
111 112 base_q = base_q.filter(or_(
112 113 User.username.ilike(like_expression),
113 114 User._email.ilike(like_expression),
114 115 User.name.ilike(like_expression),
115 116 User.lastname.ilike(like_expression),
116 117 ))
117 118
118 119 users_data_total_filtered_count = base_q.count()
119 120
120 121 sort_col = getattr(User, order_by, None)
121 122 if sort_col:
122 123 if order_dir == 'asc':
123 124 # handle null values properly to order by NULL last
124 125 if order_by in ['last_activity']:
125 126 sort_col = coalesce(sort_col, datetime.date.max)
126 127 sort_col = sort_col.asc()
127 128 else:
128 129 # handle null values properly to order by NULL last
129 130 if order_by in ['last_activity']:
130 131 sort_col = coalesce(sort_col, datetime.date.min)
131 132 sort_col = sort_col.desc()
132 133
133 134 base_q = base_q.order_by(sort_col)
134 135 base_q = base_q.offset(start).limit(limit)
135 136
136 137 users_list = base_q.all()
137 138
138 139 users_data = []
139 140 for user in users_list:
140 141 users_data.append({
141 142 "username": h.gravatar_with_user(self.request, user.username),
142 143 "email": user.email,
143 144 "first_name": user.first_name,
144 145 "last_name": user.last_name,
145 146 "last_login": h.format_date(user.last_login),
146 147 "last_activity": h.format_date(user.last_activity),
147 148 "active": h.bool2icon(user.active),
148 149 "active_raw": user.active,
149 150 "admin": h.bool2icon(user.admin),
150 151 "extern_type": user.extern_type,
151 152 "extern_name": user.extern_name,
152 153 "action": user_actions(user.user_id, user.username),
153 154 })
154 155
155 156 data = ({
156 157 'draw': draw,
157 158 'data': users_data,
158 159 'recordsTotal': users_data_total_count,
159 160 'recordsFiltered': users_data_total_filtered_count,
160 161 })
161 162
162 163 return data
163 164
164 165 @LoginRequired()
165 166 @HasPermissionAllDecorator('hg.admin')
166 167 @view_config(
167 168 route_name='edit_user_auth_tokens', request_method='GET',
168 169 renderer='rhodecode:templates/admin/users/user_edit.mako')
169 170 def auth_tokens(self):
170 171 _ = self.request.translate
171 172 c = self.load_default_context()
172 173
173 174 user_id = self.request.matchdict.get('user_id')
174 175 c.user = User.get_or_404(user_id)
175 176 self._redirect_for_default_user(c.user.username)
176 177
177 178 c.active = 'auth_tokens'
178 179
179 180 c.lifetime_values = [
180 181 (str(-1), _('forever')),
181 182 (str(5), _('5 minutes')),
182 183 (str(60), _('1 hour')),
183 184 (str(60 * 24), _('1 day')),
184 185 (str(60 * 24 * 30), _('1 month')),
185 186 ]
186 187 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
187 188 c.role_values = [
188 189 (x, AuthTokenModel.cls._get_role_name(x))
189 190 for x in AuthTokenModel.cls.ROLES]
190 191 c.role_options = [(c.role_values, _("Role"))]
191 192 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
192 193 c.user.user_id, show_expired=True)
193 194 return self._get_template_context(c)
194 195
195 196 def maybe_attach_token_scope(self, token):
196 197 # implemented in EE edition
197 198 pass
198 199
199 200 @LoginRequired()
200 201 @HasPermissionAllDecorator('hg.admin')
201 202 @CSRFRequired()
202 203 @view_config(
203 204 route_name='edit_user_auth_tokens_add', request_method='POST')
204 205 def auth_tokens_add(self):
205 206 _ = self.request.translate
206 207 c = self.load_default_context()
207 208
208 209 user_id = self.request.matchdict.get('user_id')
209 210 c.user = User.get_or_404(user_id)
210 211
211 212 self._redirect_for_default_user(c.user.username)
212 213
213 214 user_data = c.user.get_api_data()
214 215 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
215 216 description = self.request.POST.get('description')
216 217 role = self.request.POST.get('role')
217 218
218 219 token = AuthTokenModel().create(
219 220 c.user.user_id, description, lifetime, role)
220 221 token_data = token.get_api_data()
221 222
222 223 self.maybe_attach_token_scope(token)
223 224 audit_logger.store_web(
224 225 'user.edit.token.add', action_data={
225 226 'data': {'token': token_data, 'user': user_data}},
226 227 user=self._rhodecode_user, )
227 228 Session().commit()
228 229
229 230 h.flash(_("Auth token successfully created"), category='success')
230 231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
231 232
232 233 @LoginRequired()
233 234 @HasPermissionAllDecorator('hg.admin')
234 235 @CSRFRequired()
235 236 @view_config(
236 237 route_name='edit_user_auth_tokens_delete', request_method='POST')
237 238 def auth_tokens_delete(self):
238 239 _ = self.request.translate
239 240 c = self.load_default_context()
240 241
241 242 user_id = self.request.matchdict.get('user_id')
242 243 c.user = User.get_or_404(user_id)
243 244 self._redirect_for_default_user(c.user.username)
244 245 user_data = c.user.get_api_data()
245 246
246 247 del_auth_token = self.request.POST.get('del_auth_token')
247 248
248 249 if del_auth_token:
249 250 token = UserApiKeys.get_or_404(del_auth_token)
250 251 token_data = token.get_api_data()
251 252
252 253 AuthTokenModel().delete(del_auth_token, c.user.user_id)
253 254 audit_logger.store_web(
254 255 'user.edit.token.delete', action_data={
255 256 'data': {'token': token_data, 'user': user_data}},
256 257 user=self._rhodecode_user,)
257 258 Session().commit()
258 259 h.flash(_("Auth token successfully deleted"), category='success')
259 260
260 261 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
261 262
262 263 @LoginRequired()
263 264 @HasPermissionAllDecorator('hg.admin')
264 265 @view_config(
265 266 route_name='edit_user_ssh_keys', request_method='GET',
266 267 renderer='rhodecode:templates/admin/users/user_edit.mako')
267 268 def ssh_keys(self):
268 269 _ = self.request.translate
269 270 c = self.load_default_context()
270 271
271 272 user_id = self.request.matchdict.get('user_id')
272 273 c.user = User.get_or_404(user_id)
273 274 self._redirect_for_default_user(c.user.username)
274 275
275 276 c.active = 'ssh_keys'
276 277 c.default_key = self.request.GET.get('default_key')
277 278 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
278 279 return self._get_template_context(c)
279 280
280 281 @LoginRequired()
281 282 @HasPermissionAllDecorator('hg.admin')
282 283 @view_config(
283 284 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
284 285 renderer='rhodecode:templates/admin/users/user_edit.mako')
285 286 def ssh_keys_generate_keypair(self):
286 287 _ = self.request.translate
287 288 c = self.load_default_context()
288 289
289 290 user_id = self.request.matchdict.get('user_id')
290 291 c.user = User.get_or_404(user_id)
291 292 self._redirect_for_default_user(c.user.username)
292 293
293 294 c.active = 'ssh_keys_generate'
294 295 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
295 296 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
296 297
297 298 return self._get_template_context(c)
298 299
299 300 @LoginRequired()
300 301 @HasPermissionAllDecorator('hg.admin')
301 302 @CSRFRequired()
302 303 @view_config(
303 304 route_name='edit_user_ssh_keys_add', request_method='POST')
304 305 def ssh_keys_add(self):
305 306 _ = self.request.translate
306 307 c = self.load_default_context()
307 308
308 309 user_id = self.request.matchdict.get('user_id')
309 310 c.user = User.get_or_404(user_id)
310 311
311 312 self._redirect_for_default_user(c.user.username)
312 313
313 314 user_data = c.user.get_api_data()
314 315 key_data = self.request.POST.get('key_data')
315 316 description = self.request.POST.get('description')
316 317
317 318 try:
318 319 if not key_data:
319 320 raise ValueError('Please add a valid public key')
320 321
321 322 key = SshKeyModel().parse_key(key_data.strip())
322 323 fingerprint = key.hash_md5()
323 324
324 325 ssh_key = SshKeyModel().create(
325 326 c.user.user_id, fingerprint, key_data, description)
326 327 ssh_key_data = ssh_key.get_api_data()
327 328
328 329 audit_logger.store_web(
329 330 'user.edit.ssh_key.add', action_data={
330 331 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
331 332 user=self._rhodecode_user, )
332 333 Session().commit()
333 334
334 335 # Trigger an event on change of keys.
335 336 trigger(SshKeyFileChangeEvent(), self.request.registry)
336 337
337 338 h.flash(_("Ssh Key successfully created"), category='success')
338 339
339 340 except IntegrityError:
340 341 log.exception("Exception during ssh key saving")
341 342 h.flash(_('An error occurred during ssh key saving: {}').format(
342 343 'Such key already exists, please use a different one'),
343 344 category='error')
344 345 except Exception as e:
345 346 log.exception("Exception during ssh key saving")
346 347 h.flash(_('An error occurred during ssh key saving: {}').format(e),
347 348 category='error')
348 349
349 350 return HTTPFound(
350 351 h.route_path('edit_user_ssh_keys', user_id=user_id))
351 352
352 353 @LoginRequired()
353 354 @HasPermissionAllDecorator('hg.admin')
354 355 @CSRFRequired()
355 356 @view_config(
356 357 route_name='edit_user_ssh_keys_delete', request_method='POST')
357 358 def ssh_keys_delete(self):
358 359 _ = self.request.translate
359 360 c = self.load_default_context()
360 361
361 362 user_id = self.request.matchdict.get('user_id')
362 363 c.user = User.get_or_404(user_id)
363 364 self._redirect_for_default_user(c.user.username)
364 365 user_data = c.user.get_api_data()
365 366
366 367 del_ssh_key = self.request.POST.get('del_ssh_key')
367 368
368 369 if del_ssh_key:
369 370 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
370 371 ssh_key_data = ssh_key.get_api_data()
371 372
372 373 SshKeyModel().delete(del_ssh_key, c.user.user_id)
373 374 audit_logger.store_web(
374 375 'user.edit.ssh_key.delete', action_data={
375 376 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
376 377 user=self._rhodecode_user,)
377 378 Session().commit()
378 379 # Trigger an event on change of keys.
379 380 trigger(SshKeyFileChangeEvent(), self.request.registry)
380 381 h.flash(_("Ssh key successfully deleted"), category='success')
381 382
382 383 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
383 384
384 385 @LoginRequired()
385 386 @HasPermissionAllDecorator('hg.admin')
386 387 @view_config(
387 388 route_name='edit_user_emails', request_method='GET',
388 389 renderer='rhodecode:templates/admin/users/user_edit.mako')
389 390 def emails(self):
390 391 _ = self.request.translate
391 392 c = self.load_default_context()
392 393
393 394 user_id = self.request.matchdict.get('user_id')
394 395 c.user = User.get_or_404(user_id)
395 396 self._redirect_for_default_user(c.user.username)
396 397
397 398 c.active = 'emails'
398 399 c.user_email_map = UserEmailMap.query() \
399 400 .filter(UserEmailMap.user == c.user).all()
400 401
401 402 return self._get_template_context(c)
402 403
403 404 @LoginRequired()
404 405 @HasPermissionAllDecorator('hg.admin')
405 406 @CSRFRequired()
406 407 @view_config(
407 408 route_name='edit_user_emails_add', request_method='POST')
408 409 def emails_add(self):
409 410 _ = self.request.translate
410 411 c = self.load_default_context()
411 412
412 413 user_id = self.request.matchdict.get('user_id')
413 414 c.user = User.get_or_404(user_id)
414 415 self._redirect_for_default_user(c.user.username)
415 416
416 417 email = self.request.POST.get('new_email')
417 418 user_data = c.user.get_api_data()
418 419 try:
419 420 UserModel().add_extra_email(c.user.user_id, email)
420 421 audit_logger.store_web(
421 422 'user.edit.email.add', action_data={'email': email, 'user': user_data},
422 423 user=self._rhodecode_user)
423 424 Session().commit()
424 425 h.flash(_("Added new email address `%s` for user account") % email,
425 426 category='success')
426 427 except formencode.Invalid as error:
427 428 h.flash(h.escape(error.error_dict['email']), category='error')
428 429 except Exception:
429 430 log.exception("Exception during email saving")
430 431 h.flash(_('An error occurred during email saving'),
431 432 category='error')
432 433 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
433 434
434 435 @LoginRequired()
435 436 @HasPermissionAllDecorator('hg.admin')
436 437 @CSRFRequired()
437 438 @view_config(
438 439 route_name='edit_user_emails_delete', request_method='POST')
439 440 def emails_delete(self):
440 441 _ = self.request.translate
441 442 c = self.load_default_context()
442 443
443 444 user_id = self.request.matchdict.get('user_id')
444 445 c.user = User.get_or_404(user_id)
445 446 self._redirect_for_default_user(c.user.username)
446 447
447 448 email_id = self.request.POST.get('del_email_id')
448 449 user_model = UserModel()
449 450
450 451 email = UserEmailMap.query().get(email_id).email
451 452 user_data = c.user.get_api_data()
452 453 user_model.delete_extra_email(c.user.user_id, email_id)
453 454 audit_logger.store_web(
454 455 'user.edit.email.delete', action_data={'email': email, 'user': user_data},
455 456 user=self._rhodecode_user)
456 457 Session().commit()
457 458 h.flash(_("Removed email address from user account"),
458 459 category='success')
459 460 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
460 461
461 462 @LoginRequired()
462 463 @HasPermissionAllDecorator('hg.admin')
463 464 @view_config(
464 465 route_name='edit_user_ips', request_method='GET',
465 466 renderer='rhodecode:templates/admin/users/user_edit.mako')
466 467 def ips(self):
467 468 _ = self.request.translate
468 469 c = self.load_default_context()
469 470
470 471 user_id = self.request.matchdict.get('user_id')
471 472 c.user = User.get_or_404(user_id)
472 473 self._redirect_for_default_user(c.user.username)
473 474
474 475 c.active = 'ips'
475 476 c.user_ip_map = UserIpMap.query() \
476 477 .filter(UserIpMap.user == c.user).all()
477 478
478 479 c.inherit_default_ips = c.user.inherit_default_permissions
479 480 c.default_user_ip_map = UserIpMap.query() \
480 481 .filter(UserIpMap.user == User.get_default_user()).all()
481 482
482 483 return self._get_template_context(c)
483 484
484 485 @LoginRequired()
485 486 @HasPermissionAllDecorator('hg.admin')
486 487 @CSRFRequired()
487 488 @view_config(
488 489 route_name='edit_user_ips_add', request_method='POST')
489 490 def ips_add(self):
490 491 _ = self.request.translate
491 492 c = self.load_default_context()
492 493
493 494 user_id = self.request.matchdict.get('user_id')
494 495 c.user = User.get_or_404(user_id)
495 496 # NOTE(marcink): this view is allowed for default users, as we can
496 497 # edit their IP white list
497 498
498 499 user_model = UserModel()
499 500 desc = self.request.POST.get('description')
500 501 try:
501 502 ip_list = user_model.parse_ip_range(
502 503 self.request.POST.get('new_ip'))
503 504 except Exception as e:
504 505 ip_list = []
505 506 log.exception("Exception during ip saving")
506 507 h.flash(_('An error occurred during ip saving:%s' % (e,)),
507 508 category='error')
508 509 added = []
509 510 user_data = c.user.get_api_data()
510 511 for ip in ip_list:
511 512 try:
512 513 user_model.add_extra_ip(c.user.user_id, ip, desc)
513 514 audit_logger.store_web(
514 515 'user.edit.ip.add', action_data={'ip': ip, 'user': user_data},
515 516 user=self._rhodecode_user)
516 517 Session().commit()
517 518 added.append(ip)
518 519 except formencode.Invalid as error:
519 520 msg = error.error_dict['ip']
520 521 h.flash(msg, category='error')
521 522 except Exception:
522 523 log.exception("Exception during ip saving")
523 524 h.flash(_('An error occurred during ip saving'),
524 525 category='error')
525 526 if added:
526 527 h.flash(
527 528 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
528 529 category='success')
529 530 if 'default_user' in self.request.POST:
530 531 # case for editing global IP list we do it for 'DEFAULT' user
531 532 raise HTTPFound(h.route_path('admin_permissions_ips'))
532 533 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
533 534
534 535 @LoginRequired()
535 536 @HasPermissionAllDecorator('hg.admin')
536 537 @CSRFRequired()
537 538 @view_config(
538 539 route_name='edit_user_ips_delete', request_method='POST')
539 540 def ips_delete(self):
540 541 _ = self.request.translate
541 542 c = self.load_default_context()
542 543
543 544 user_id = self.request.matchdict.get('user_id')
544 545 c.user = User.get_or_404(user_id)
545 546 # NOTE(marcink): this view is allowed for default users, as we can
546 547 # edit their IP white list
547 548
548 549 ip_id = self.request.POST.get('del_ip_id')
549 550 user_model = UserModel()
550 551 user_data = c.user.get_api_data()
551 552 ip = UserIpMap.query().get(ip_id).ip_addr
552 553 user_model.delete_extra_ip(c.user.user_id, ip_id)
553 554 audit_logger.store_web(
554 555 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
555 556 user=self._rhodecode_user)
556 557 Session().commit()
557 558 h.flash(_("Removed ip address from user whitelist"), category='success')
558 559
559 560 if 'default_user' in self.request.POST:
560 561 # case for editing global IP list we do it for 'DEFAULT' user
561 562 raise HTTPFound(h.route_path('admin_permissions_ips'))
562 563 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
563 564
564 565 @LoginRequired()
565 566 @HasPermissionAllDecorator('hg.admin')
566 567 @view_config(
567 568 route_name='edit_user_groups_management', request_method='GET',
568 569 renderer='rhodecode:templates/admin/users/user_edit.mako')
569 570 def groups_management(self):
570 571 c = self.load_default_context()
571 572
572 573 user_id = self.request.matchdict.get('user_id')
573 574 c.user = User.get_or_404(user_id)
574 575 c.data = c.user.group_member
575 576 self._redirect_for_default_user(c.user.username)
576 577 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
577 578 for group in c.user.group_member]
578 579 c.groups = json.dumps(groups)
579 580 c.active = 'groups'
580 581
581 582 return self._get_template_context(c)
582 583
583 584 @LoginRequired()
584 585 @HasPermissionAllDecorator('hg.admin')
585 586 @CSRFRequired()
586 587 @view_config(
587 588 route_name='edit_user_groups_management_updates', request_method='POST')
588 589 def groups_management_updates(self):
589 590 _ = self.request.translate
590 591 c = self.load_default_context()
591 592
592 593 user_id = self.request.matchdict.get('user_id')
593 594 c.user = User.get_or_404(user_id)
594 595 self._redirect_for_default_user(c.user.username)
595 596
596 597 user_groups = set(self.request.POST.getall('users_group_id'))
597 598 user_groups_objects = []
598 599
599 600 for ugid in user_groups:
600 601 user_groups_objects.append(
601 602 UserGroupModel().get_group(safe_int(ugid)))
602 603 user_group_model = UserGroupModel()
603 604 user_group_model.change_groups(c.user, user_groups_objects)
604 605
605 606 Session().commit()
606 607 c.active = 'user_groups_management'
607 608 h.flash(_("Groups successfully changed"), category='success')
608 609
609 610 return HTTPFound(h.route_path(
610 611 'edit_user_groups_management', user_id=user_id))
611 612
612 613 @LoginRequired()
613 614 @HasPermissionAllDecorator('hg.admin')
614 615 @view_config(
615 616 route_name='edit_user_audit_logs', request_method='GET',
616 617 renderer='rhodecode:templates/admin/users/user_edit.mako')
617 618 def user_audit_logs(self):
618 619 _ = self.request.translate
619 620 c = self.load_default_context()
620 621
621 622 user_id = self.request.matchdict.get('user_id')
622 623 c.user = User.get_or_404(user_id)
623 624 self._redirect_for_default_user(c.user.username)
624 625 c.active = 'audit'
625 626
626 627 p = safe_int(self.request.GET.get('page', 1), 1)
627 628
628 629 filter_term = self.request.GET.get('filter')
629 630 user_log = UserModel().get_user_log(c.user, filter_term)
630 631
631 632 def url_generator(**kw):
632 633 if filter_term:
633 634 kw['filter'] = filter_term
634 635 return self.request.current_route_path(_query=kw)
635 636
636 637 c.audit_logs = h.Page(
637 638 user_log, page=p, items_per_page=10, url=url_generator)
638 639 c.filter_term = filter_term
639 640 return self._get_template_context(c)
640 641
641 642 @LoginRequired()
642 643 @HasPermissionAllDecorator('hg.admin')
643 644 @view_config(
644 645 route_name='edit_user_perms_summary', request_method='GET',
645 646 renderer='rhodecode:templates/admin/users/user_edit.mako')
646 647 def user_perms_summary(self):
647 648 _ = self.request.translate
648 649 c = self.load_default_context()
649 650
650 651 user_id = self.request.matchdict.get('user_id')
651 652 c.user = User.get_or_404(user_id)
652 653 self._redirect_for_default_user(c.user.username)
653 654
654 655 c.active = 'perms_summary'
655 656 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
656 657
657 658 return self._get_template_context(c)
658 659
659 660 @LoginRequired()
660 661 @HasPermissionAllDecorator('hg.admin')
661 662 @view_config(
662 663 route_name='edit_user_perms_summary_json', request_method='GET',
663 664 renderer='json_ext')
664 665 def user_perms_summary_json(self):
665 666 self.load_default_context()
666 667
667 668 user_id = self.request.matchdict.get('user_id')
668 669 user = User.get_or_404(user_id)
669 670 self._redirect_for_default_user(user.username)
670 671
671 672 perm_user = user.AuthUser(ip_addr=self.request.remote_addr)
672 673
673 674 return perm_user.permissions
@@ -1,412 +1,413 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2013-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import time
22 22 import logging
23 23
24 24 import formencode
25 import formencode.htmlfill
25 26 import peppercorn
26 27
27 28 from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden, HTTPFound
28 29 from pyramid.view import view_config
29 30 from pyramid.renderers import render
30 31 from pyramid.response import Response
31 32
32 33 from rhodecode.apps._base import BaseAppView
33 34 from rhodecode.lib import helpers as h
34 35 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
35 36 from rhodecode.lib.utils2 import time_to_datetime
36 37 from rhodecode.lib.ext_json import json
37 38 from rhodecode.lib.vcs.exceptions import VCSError, NodeNotChangedError
38 39 from rhodecode.model.gist import GistModel
39 40 from rhodecode.model.meta import Session
40 41 from rhodecode.model.db import Gist, User, or_
41 42 from rhodecode.model import validation_schema
42 43 from rhodecode.model.validation_schema.schemas import gist_schema
43 44
44 45
45 46 log = logging.getLogger(__name__)
46 47
47 48
48 49 class GistView(BaseAppView):
49 50
50 51 def load_default_context(self):
51 52 _ = self.request.translate
52 53 c = self._get_local_tmpl_context()
53 54 c.user = c.auth_user.get_instance()
54 55
55 56 c.lifetime_values = [
56 57 (-1, _('forever')),
57 58 (5, _('5 minutes')),
58 59 (60, _('1 hour')),
59 60 (60 * 24, _('1 day')),
60 61 (60 * 24 * 30, _('1 month')),
61 62 ]
62 63
63 64 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
64 65 c.acl_options = [
65 66 (Gist.ACL_LEVEL_PRIVATE, _("Requires registered account")),
66 67 (Gist.ACL_LEVEL_PUBLIC, _("Can be accessed by anonymous users"))
67 68 ]
68 69
69 70 self._register_global_c(c)
70 71 return c
71 72
72 73 @LoginRequired()
73 74 @view_config(
74 75 route_name='gists_show', request_method='GET',
75 76 renderer='rhodecode:templates/admin/gists/index.mako')
76 77 def gist_show_all(self):
77 78 c = self.load_default_context()
78 79
79 80 not_default_user = self._rhodecode_user.username != User.DEFAULT_USER
80 81 c.show_private = self.request.GET.get('private') and not_default_user
81 82 c.show_public = self.request.GET.get('public') and not_default_user
82 83 c.show_all = self.request.GET.get('all') and self._rhodecode_user.admin
83 84
84 85 gists = _gists = Gist().query()\
85 86 .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
86 87 .order_by(Gist.created_on.desc())
87 88
88 89 c.active = 'public'
89 90 # MY private
90 91 if c.show_private and not c.show_public:
91 92 gists = _gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
92 93 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
93 94 c.active = 'my_private'
94 95 # MY public
95 96 elif c.show_public and not c.show_private:
96 97 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
97 98 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
98 99 c.active = 'my_public'
99 100 # MY public+private
100 101 elif c.show_private and c.show_public:
101 102 gists = _gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
102 103 Gist.gist_type == Gist.GIST_PRIVATE))\
103 104 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
104 105 c.active = 'my_all'
105 106 # Show all by super-admin
106 107 elif c.show_all:
107 108 c.active = 'all'
108 109 gists = _gists
109 110
110 111 # default show ALL public gists
111 112 if not c.show_public and not c.show_private and not c.show_all:
112 113 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
113 114 c.active = 'public'
114 115
115 116 _render = self.request.get_partial_renderer(
116 117 'data_table/_dt_elements.mako')
117 118
118 119 data = []
119 120
120 121 for gist in gists:
121 122 data.append({
122 123 'created_on': _render('gist_created', gist.created_on),
123 124 'created_on_raw': gist.created_on,
124 125 'type': _render('gist_type', gist.gist_type),
125 126 'access_id': _render('gist_access_id', gist.gist_access_id, gist.owner.full_contact),
126 127 'author': _render('gist_author', gist.owner.full_contact, gist.created_on, gist.gist_expires),
127 128 'author_raw': h.escape(gist.owner.full_contact),
128 129 'expires': _render('gist_expires', gist.gist_expires),
129 130 'description': _render('gist_description', gist.gist_description)
130 131 })
131 132 c.data = json.dumps(data)
132 133
133 134 return self._get_template_context(c)
134 135
135 136 @LoginRequired()
136 137 @NotAnonymous()
137 138 @view_config(
138 139 route_name='gists_new', request_method='GET',
139 140 renderer='rhodecode:templates/admin/gists/new.mako')
140 141 def gist_new(self):
141 142 c = self.load_default_context()
142 143 return self._get_template_context(c)
143 144
144 145 @LoginRequired()
145 146 @NotAnonymous()
146 147 @CSRFRequired()
147 148 @view_config(
148 149 route_name='gists_create', request_method='POST',
149 150 renderer='rhodecode:templates/admin/gists/new.mako')
150 151 def gist_create(self):
151 152 _ = self.request.translate
152 153 c = self.load_default_context()
153 154
154 155 data = dict(self.request.POST)
155 156 data['filename'] = data.get('filename') or Gist.DEFAULT_FILENAME
156 157 data['nodes'] = [{
157 158 'filename': data['filename'],
158 159 'content': data.get('content'),
159 160 'mimetype': data.get('mimetype') # None is autodetect
160 161 }]
161 162
162 163 data['gist_type'] = (
163 164 Gist.GIST_PUBLIC if data.get('public') else Gist.GIST_PRIVATE)
164 165 data['gist_acl_level'] = (
165 166 data.get('gist_acl_level') or Gist.ACL_LEVEL_PRIVATE)
166 167
167 168 schema = gist_schema.GistSchema().bind(
168 169 lifetime_options=[x[0] for x in c.lifetime_values])
169 170
170 171 try:
171 172
172 173 schema_data = schema.deserialize(data)
173 174 # convert to safer format with just KEYs so we sure no duplicates
174 175 schema_data['nodes'] = gist_schema.sequence_to_nodes(
175 176 schema_data['nodes'])
176 177
177 178 gist = GistModel().create(
178 179 gist_id=schema_data['gistid'], # custom access id not real ID
179 180 description=schema_data['description'],
180 181 owner=self._rhodecode_user.user_id,
181 182 gist_mapping=schema_data['nodes'],
182 183 gist_type=schema_data['gist_type'],
183 184 lifetime=schema_data['lifetime'],
184 185 gist_acl_level=schema_data['gist_acl_level']
185 186 )
186 187 Session().commit()
187 188 new_gist_id = gist.gist_access_id
188 189 except validation_schema.Invalid as errors:
189 190 defaults = data
190 191 errors = errors.asdict()
191 192
192 193 if 'nodes.0.content' in errors:
193 194 errors['content'] = errors['nodes.0.content']
194 195 del errors['nodes.0.content']
195 196 if 'nodes.0.filename' in errors:
196 197 errors['filename'] = errors['nodes.0.filename']
197 198 del errors['nodes.0.filename']
198 199
199 200 data = render('rhodecode:templates/admin/gists/new.mako',
200 201 self._get_template_context(c), self.request)
201 202 html = formencode.htmlfill.render(
202 203 data,
203 204 defaults=defaults,
204 205 errors=errors,
205 206 prefix_error=False,
206 207 encoding="UTF-8",
207 208 force_defaults=False
208 209 )
209 210 return Response(html)
210 211
211 212 except Exception:
212 213 log.exception("Exception while trying to create a gist")
213 214 h.flash(_('Error occurred during gist creation'), category='error')
214 215 raise HTTPFound(h.route_url('gists_new'))
215 216 raise HTTPFound(h.route_url('gist_show', gist_id=new_gist_id))
216 217
217 218 @LoginRequired()
218 219 @NotAnonymous()
219 220 @CSRFRequired()
220 221 @view_config(
221 222 route_name='gist_delete', request_method='POST')
222 223 def gist_delete(self):
223 224 _ = self.request.translate
224 225 gist_id = self.request.matchdict['gist_id']
225 226
226 227 c = self.load_default_context()
227 228 c.gist = Gist.get_or_404(gist_id)
228 229
229 230 owner = c.gist.gist_owner == self._rhodecode_user.user_id
230 231 if not (h.HasPermissionAny('hg.admin')() or owner):
231 232 log.warning('Deletion of Gist was forbidden '
232 233 'by unauthorized user: `%s`', self._rhodecode_user)
233 234 raise HTTPNotFound()
234 235
235 236 GistModel().delete(c.gist)
236 237 Session().commit()
237 238 h.flash(_('Deleted gist %s') % c.gist.gist_access_id, category='success')
238 239
239 240 raise HTTPFound(h.route_url('gists_show'))
240 241
241 242 def _get_gist(self, gist_id):
242 243
243 244 gist = Gist.get_or_404(gist_id)
244 245
245 246 # Check if this gist is expired
246 247 if gist.gist_expires != -1:
247 248 if time.time() > gist.gist_expires:
248 249 log.error(
249 250 'Gist expired at %s', time_to_datetime(gist.gist_expires))
250 251 raise HTTPNotFound()
251 252
252 253 # check if this gist requires a login
253 254 is_default_user = self._rhodecode_user.username == User.DEFAULT_USER
254 255 if gist.acl_level == Gist.ACL_LEVEL_PRIVATE and is_default_user:
255 256 log.error("Anonymous user %s tried to access protected gist `%s`",
256 257 self._rhodecode_user, gist_id)
257 258 raise HTTPNotFound()
258 259 return gist
259 260
260 261 @LoginRequired()
261 262 @view_config(
262 263 route_name='gist_show', request_method='GET',
263 264 renderer='rhodecode:templates/admin/gists/show.mako')
264 265 @view_config(
265 266 route_name='gist_show_rev', request_method='GET',
266 267 renderer='rhodecode:templates/admin/gists/show.mako')
267 268 @view_config(
268 269 route_name='gist_show_formatted', request_method='GET',
269 270 renderer=None)
270 271 @view_config(
271 272 route_name='gist_show_formatted_path', request_method='GET',
272 273 renderer=None)
273 274 def gist_show(self):
274 275 gist_id = self.request.matchdict['gist_id']
275 276
276 277 # TODO(marcink): expose those via matching dict
277 278 revision = self.request.matchdict.get('revision', 'tip')
278 279 f_path = self.request.matchdict.get('f_path', None)
279 280 return_format = self.request.matchdict.get('format')
280 281
281 282 c = self.load_default_context()
282 283 c.gist = self._get_gist(gist_id)
283 284 c.render = not self.request.GET.get('no-render', False)
284 285
285 286 try:
286 287 c.file_last_commit, c.files = GistModel().get_gist_files(
287 288 gist_id, revision=revision)
288 289 except VCSError:
289 290 log.exception("Exception in gist show")
290 291 raise HTTPNotFound()
291 292
292 293 if return_format == 'raw':
293 294 content = '\n\n'.join([f.content for f in c.files
294 295 if (f_path is None or f.path == f_path)])
295 296 response = Response(content)
296 297 response.content_type = 'text/plain'
297 298 return response
298 299
299 300 return self._get_template_context(c)
300 301
301 302 @LoginRequired()
302 303 @NotAnonymous()
303 304 @view_config(
304 305 route_name='gist_edit', request_method='GET',
305 306 renderer='rhodecode:templates/admin/gists/edit.mako')
306 307 def gist_edit(self):
307 308 _ = self.request.translate
308 309 gist_id = self.request.matchdict['gist_id']
309 310 c = self.load_default_context()
310 311 c.gist = self._get_gist(gist_id)
311 312
312 313 owner = c.gist.gist_owner == self._rhodecode_user.user_id
313 314 if not (h.HasPermissionAny('hg.admin')() or owner):
314 315 raise HTTPNotFound()
315 316
316 317 try:
317 318 c.file_last_commit, c.files = GistModel().get_gist_files(gist_id)
318 319 except VCSError:
319 320 log.exception("Exception in gist edit")
320 321 raise HTTPNotFound()
321 322
322 323 if c.gist.gist_expires == -1:
323 324 expiry = _('never')
324 325 else:
325 326 # this cannot use timeago, since it's used in select2 as a value
326 327 expiry = h.age(h.time_to_datetime(c.gist.gist_expires))
327 328
328 329 c.lifetime_values.append(
329 330 (0, _('%(expiry)s - current value') % {'expiry': _(expiry)})
330 331 )
331 332
332 333 return self._get_template_context(c)
333 334
334 335 @LoginRequired()
335 336 @NotAnonymous()
336 337 @CSRFRequired()
337 338 @view_config(
338 339 route_name='gist_update', request_method='POST',
339 340 renderer='rhodecode:templates/admin/gists/edit.mako')
340 341 def gist_update(self):
341 342 _ = self.request.translate
342 343 gist_id = self.request.matchdict['gist_id']
343 344 c = self.load_default_context()
344 345 c.gist = self._get_gist(gist_id)
345 346
346 347 owner = c.gist.gist_owner == self._rhodecode_user.user_id
347 348 if not (h.HasPermissionAny('hg.admin')() or owner):
348 349 raise HTTPNotFound()
349 350
350 351 data = peppercorn.parse(self.request.POST.items())
351 352
352 353 schema = gist_schema.GistSchema()
353 354 schema = schema.bind(
354 355 # '0' is special value to leave lifetime untouched
355 356 lifetime_options=[x[0] for x in c.lifetime_values] + [0],
356 357 )
357 358
358 359 try:
359 360 schema_data = schema.deserialize(data)
360 361 # convert to safer format with just KEYs so we sure no duplicates
361 362 schema_data['nodes'] = gist_schema.sequence_to_nodes(
362 363 schema_data['nodes'])
363 364
364 365 GistModel().update(
365 366 gist=c.gist,
366 367 description=schema_data['description'],
367 368 owner=c.gist.owner,
368 369 gist_mapping=schema_data['nodes'],
369 370 lifetime=schema_data['lifetime'],
370 371 gist_acl_level=schema_data['gist_acl_level']
371 372 )
372 373
373 374 Session().commit()
374 375 h.flash(_('Successfully updated gist content'), category='success')
375 376 except NodeNotChangedError:
376 377 # raised if nothing was changed in repo itself. We anyway then
377 378 # store only DB stuff for gist
378 379 Session().commit()
379 380 h.flash(_('Successfully updated gist data'), category='success')
380 381 except validation_schema.Invalid as errors:
381 382 errors = h.escape(errors.asdict())
382 383 h.flash(_('Error occurred during update of gist {}: {}').format(
383 384 gist_id, errors), category='error')
384 385 except Exception:
385 386 log.exception("Exception in gist edit")
386 387 h.flash(_('Error occurred during update of gist %s') % gist_id,
387 388 category='error')
388 389
389 390 raise HTTPFound(h.route_url('gist_show', gist_id=gist_id))
390 391
391 392 @LoginRequired()
392 393 @NotAnonymous()
393 394 @view_config(
394 395 route_name='gist_edit_check_revision', request_method='GET',
395 396 renderer='json_ext')
396 397 def gist_edit_check_revision(self):
397 398 _ = self.request.translate
398 399 gist_id = self.request.matchdict['gist_id']
399 400 c = self.load_default_context()
400 401 c.gist = self._get_gist(gist_id)
401 402
402 403 last_rev = c.gist.scm_instance().get_commit()
403 404 success = True
404 405 revision = self.request.GET.get('revision')
405 406
406 407 if revision != last_rev.raw_id:
407 408 log.error('Last revision %s is different then submitted %s'
408 409 % (revision, last_rev))
409 410 # our gist has newer version than we
410 411 success = False
411 412
412 413 return {'success': success}
General Comments 0
You need to be logged in to leave comments. Login now