##// END OF EJS Templates
forms: fixed error handling in forms
super-admin -
r5018:b9966616 default
parent child Browse files
Show More
@@ -1,103 +1,103 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.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
30 from rhodecode.apps._base import BaseAppView
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
32 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.model.forms import DefaultsForm
34 from rhodecode.model.forms import DefaultsForm
35 from rhodecode.model.meta import Session
35 from rhodecode.model.meta import Session
36 from rhodecode import BACKENDS
36 from rhodecode import BACKENDS
37 from rhodecode.model.settings import SettingsModel
37 from rhodecode.model.settings import SettingsModel
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class AdminDefaultSettingsView(BaseAppView):
42 class AdminDefaultSettingsView(BaseAppView):
43
43
44 def load_default_context(self):
44 def load_default_context(self):
45 c = self._get_local_tmpl_context()
45 c = self._get_local_tmpl_context()
46 return c
46 return c
47
47
48 @LoginRequired()
48 @LoginRequired()
49 @HasPermissionAllDecorator('hg.admin')
49 @HasPermissionAllDecorator('hg.admin')
50 def defaults_repository_show(self):
50 def defaults_repository_show(self):
51 c = self.load_default_context()
51 c = self.load_default_context()
52 c.backends = BACKENDS.keys()
52 c.backends = BACKENDS.keys()
53 c.active = 'repositories'
53 c.active = 'repositories'
54 defaults = SettingsModel().get_default_repo_settings()
54 defaults = SettingsModel().get_default_repo_settings()
55
55
56 data = render(
56 data = render(
57 'rhodecode:templates/admin/defaults/defaults.mako',
57 'rhodecode:templates/admin/defaults/defaults.mako',
58 self._get_template_context(c), self.request)
58 self._get_template_context(c), self.request)
59 html = formencode.htmlfill.render(
59 html = formencode.htmlfill.render(
60 data,
60 data,
61 defaults=defaults,
61 defaults=defaults,
62 encoding="UTF-8",
62 encoding="UTF-8",
63 force_defaults=False
63 force_defaults=False
64 )
64 )
65 return Response(html)
65 return Response(html)
66
66
67 @LoginRequired()
67 @LoginRequired()
68 @HasPermissionAllDecorator('hg.admin')
68 @HasPermissionAllDecorator('hg.admin')
69 @CSRFRequired()
69 @CSRFRequired()
70 def defaults_repository_update(self):
70 def defaults_repository_update(self):
71 _ = self.request.translate
71 _ = self.request.translate
72 c = self.load_default_context()
72 c = self.load_default_context()
73 c.active = 'repositories'
73 c.active = 'repositories'
74 form = DefaultsForm(self.request.translate)()
74 form = DefaultsForm(self.request.translate)()
75
75
76 try:
76 try:
77 form_result = form.to_python(dict(self.request.POST))
77 form_result = form.to_python(dict(self.request.POST))
78 for k, v in form_result.items():
78 for k, v in form_result.items():
79 setting = SettingsModel().create_or_update_setting(k, v)
79 setting = SettingsModel().create_or_update_setting(k, v)
80 Session().add(setting)
80 Session().add(setting)
81 Session().commit()
81 Session().commit()
82 h.flash(_('Default settings updated successfully'),
82 h.flash(_('Default settings updated successfully'),
83 category='success')
83 category='success')
84
84
85 except formencode.Invalid as errors:
85 except formencode.Invalid as errors:
86 data = render(
86 data = render(
87 'rhodecode:templates/admin/defaults/defaults.mako',
87 'rhodecode:templates/admin/defaults/defaults.mako',
88 self._get_template_context(c), self.request)
88 self._get_template_context(c), self.request)
89 html = formencode.htmlfill.render(
89 html = formencode.htmlfill.render(
90 data,
90 data,
91 defaults=errors.value,
91 defaults=errors.value,
92 errors=errors.error_dict or {},
92 errors=errors.unpack_errors() or {},
93 prefix_error=False,
93 prefix_error=False,
94 encoding="UTF-8",
94 encoding="UTF-8",
95 force_defaults=False
95 force_defaults=False
96 )
96 )
97 return Response(html)
97 return Response(html)
98 except Exception:
98 except Exception:
99 log.exception('Exception in update action')
99 log.exception('Exception in update action')
100 h.flash(_('Error occurred during update of default values'),
100 h.flash(_('Error occurred during update of default values'),
101 category='error')
101 category='error')
102
102
103 raise HTTPFound(h.route_path('admin_defaults_repositories'))
103 raise HTTPFound(h.route_path('admin_defaults_repositories'))
@@ -1,479 +1,479 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25 import datetime
25 import datetime
26 from pyramid.interfaces import IRoutesMapper
26 from pyramid.interfaces import IRoutesMapper
27
27
28 from pyramid.httpexceptions import HTTPFound
28 from pyramid.httpexceptions import HTTPFound
29 from pyramid.renderers import render
29 from pyramid.renderers import render
30 from pyramid.response import Response
30 from pyramid.response import Response
31
31
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 from rhodecode import events
34 from rhodecode import events
35
35
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import (
37 from rhodecode.lib.auth import (
38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
39 from rhodecode.lib.utils2 import aslist, safe_unicode
39 from rhodecode.lib.utils2 import aslist, safe_unicode
40 from rhodecode.model.db import (
40 from rhodecode.model.db import (
41 or_, coalesce, User, UserIpMap, UserSshKeys)
41 or_, coalesce, User, UserIpMap, UserSshKeys)
42 from rhodecode.model.forms import (
42 from rhodecode.model.forms import (
43 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
43 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
44 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
45 from rhodecode.model.permission import PermissionModel
45 from rhodecode.model.permission import PermissionModel
46 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.settings import SettingsModel
47
47
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class AdminPermissionsView(BaseAppView, DataGridAppView):
52 class AdminPermissionsView(BaseAppView, DataGridAppView):
53 def load_default_context(self):
53 def load_default_context(self):
54 c = self._get_local_tmpl_context()
54 c = self._get_local_tmpl_context()
55 PermissionModel().set_global_permission_choices(
55 PermissionModel().set_global_permission_choices(
56 c, gettext_translator=self.request.translate)
56 c, gettext_translator=self.request.translate)
57 return c
57 return c
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasPermissionAllDecorator('hg.admin')
60 @HasPermissionAllDecorator('hg.admin')
61 def permissions_application(self):
61 def permissions_application(self):
62 c = self.load_default_context()
62 c = self.load_default_context()
63 c.active = 'application'
63 c.active = 'application'
64
64
65 c.user = User.get_default_user(refresh=True)
65 c.user = User.get_default_user(refresh=True)
66
66
67 app_settings = c.rc_config
67 app_settings = c.rc_config
68
68
69 defaults = {
69 defaults = {
70 'anonymous': c.user.active,
70 'anonymous': c.user.active,
71 'default_register_message': app_settings.get(
71 'default_register_message': app_settings.get(
72 'rhodecode_register_message')
72 'rhodecode_register_message')
73 }
73 }
74 defaults.update(c.user.get_default_perms())
74 defaults.update(c.user.get_default_perms())
75
75
76 data = render('rhodecode:templates/admin/permissions/permissions.mako',
76 data = render('rhodecode:templates/admin/permissions/permissions.mako',
77 self._get_template_context(c), self.request)
77 self._get_template_context(c), self.request)
78 html = formencode.htmlfill.render(
78 html = formencode.htmlfill.render(
79 data,
79 data,
80 defaults=defaults,
80 defaults=defaults,
81 encoding="UTF-8",
81 encoding="UTF-8",
82 force_defaults=False
82 force_defaults=False
83 )
83 )
84 return Response(html)
84 return Response(html)
85
85
86 @LoginRequired()
86 @LoginRequired()
87 @HasPermissionAllDecorator('hg.admin')
87 @HasPermissionAllDecorator('hg.admin')
88 @CSRFRequired()
88 @CSRFRequired()
89 def permissions_application_update(self):
89 def permissions_application_update(self):
90 _ = self.request.translate
90 _ = self.request.translate
91 c = self.load_default_context()
91 c = self.load_default_context()
92 c.active = 'application'
92 c.active = 'application'
93
93
94 _form = ApplicationPermissionsForm(
94 _form = ApplicationPermissionsForm(
95 self.request.translate,
95 self.request.translate,
96 [x[0] for x in c.register_choices],
96 [x[0] for x in c.register_choices],
97 [x[0] for x in c.password_reset_choices],
97 [x[0] for x in c.password_reset_choices],
98 [x[0] for x in c.extern_activate_choices])()
98 [x[0] for x in c.extern_activate_choices])()
99
99
100 try:
100 try:
101 form_result = _form.to_python(dict(self.request.POST))
101 form_result = _form.to_python(dict(self.request.POST))
102 form_result.update({'perm_user_name': User.DEFAULT_USER})
102 form_result.update({'perm_user_name': User.DEFAULT_USER})
103 PermissionModel().update_application_permissions(form_result)
103 PermissionModel().update_application_permissions(form_result)
104
104
105 settings = [
105 settings = [
106 ('register_message', 'default_register_message'),
106 ('register_message', 'default_register_message'),
107 ]
107 ]
108 for setting, form_key in settings:
108 for setting, form_key in settings:
109 sett = SettingsModel().create_or_update_setting(
109 sett = SettingsModel().create_or_update_setting(
110 setting, form_result[form_key])
110 setting, form_result[form_key])
111 Session().add(sett)
111 Session().add(sett)
112
112
113 Session().commit()
113 Session().commit()
114 h.flash(_('Application permissions updated successfully'),
114 h.flash(_('Application permissions updated successfully'),
115 category='success')
115 category='success')
116
116
117 except formencode.Invalid as errors:
117 except formencode.Invalid as errors:
118 defaults = errors.value
118 defaults = errors.value
119
119
120 data = render(
120 data = render(
121 'rhodecode:templates/admin/permissions/permissions.mako',
121 'rhodecode:templates/admin/permissions/permissions.mako',
122 self._get_template_context(c), self.request)
122 self._get_template_context(c), self.request)
123 html = formencode.htmlfill.render(
123 html = formencode.htmlfill.render(
124 data,
124 data,
125 defaults=defaults,
125 defaults=defaults,
126 errors=errors.error_dict or {},
126 errors=errors.unpack_errors() or {},
127 prefix_error=False,
127 prefix_error=False,
128 encoding="UTF-8",
128 encoding="UTF-8",
129 force_defaults=False
129 force_defaults=False
130 )
130 )
131 return Response(html)
131 return Response(html)
132
132
133 except Exception:
133 except Exception:
134 log.exception("Exception during update of permissions")
134 log.exception("Exception during update of permissions")
135 h.flash(_('Error occurred during update of permissions'),
135 h.flash(_('Error occurred during update of permissions'),
136 category='error')
136 category='error')
137
137
138 affected_user_ids = [User.get_default_user_id()]
138 affected_user_ids = [User.get_default_user_id()]
139 PermissionModel().trigger_permission_flush(affected_user_ids)
139 PermissionModel().trigger_permission_flush(affected_user_ids)
140
140
141 raise HTTPFound(h.route_path('admin_permissions_application'))
141 raise HTTPFound(h.route_path('admin_permissions_application'))
142
142
143 @LoginRequired()
143 @LoginRequired()
144 @HasPermissionAllDecorator('hg.admin')
144 @HasPermissionAllDecorator('hg.admin')
145 def permissions_objects(self):
145 def permissions_objects(self):
146 c = self.load_default_context()
146 c = self.load_default_context()
147 c.active = 'objects'
147 c.active = 'objects'
148
148
149 c.user = User.get_default_user(refresh=True)
149 c.user = User.get_default_user(refresh=True)
150 defaults = {}
150 defaults = {}
151 defaults.update(c.user.get_default_perms())
151 defaults.update(c.user.get_default_perms())
152
152
153 data = render(
153 data = render(
154 'rhodecode:templates/admin/permissions/permissions.mako',
154 'rhodecode:templates/admin/permissions/permissions.mako',
155 self._get_template_context(c), self.request)
155 self._get_template_context(c), self.request)
156 html = formencode.htmlfill.render(
156 html = formencode.htmlfill.render(
157 data,
157 data,
158 defaults=defaults,
158 defaults=defaults,
159 encoding="UTF-8",
159 encoding="UTF-8",
160 force_defaults=False
160 force_defaults=False
161 )
161 )
162 return Response(html)
162 return Response(html)
163
163
164 @LoginRequired()
164 @LoginRequired()
165 @HasPermissionAllDecorator('hg.admin')
165 @HasPermissionAllDecorator('hg.admin')
166 @CSRFRequired()
166 @CSRFRequired()
167 def permissions_objects_update(self):
167 def permissions_objects_update(self):
168 _ = self.request.translate
168 _ = self.request.translate
169 c = self.load_default_context()
169 c = self.load_default_context()
170 c.active = 'objects'
170 c.active = 'objects'
171
171
172 _form = ObjectPermissionsForm(
172 _form = ObjectPermissionsForm(
173 self.request.translate,
173 self.request.translate,
174 [x[0] for x in c.repo_perms_choices],
174 [x[0] for x in c.repo_perms_choices],
175 [x[0] for x in c.group_perms_choices],
175 [x[0] for x in c.group_perms_choices],
176 [x[0] for x in c.user_group_perms_choices],
176 [x[0] for x in c.user_group_perms_choices],
177 )()
177 )()
178
178
179 try:
179 try:
180 form_result = _form.to_python(dict(self.request.POST))
180 form_result = _form.to_python(dict(self.request.POST))
181 form_result.update({'perm_user_name': User.DEFAULT_USER})
181 form_result.update({'perm_user_name': User.DEFAULT_USER})
182 PermissionModel().update_object_permissions(form_result)
182 PermissionModel().update_object_permissions(form_result)
183
183
184 Session().commit()
184 Session().commit()
185 h.flash(_('Object permissions updated successfully'),
185 h.flash(_('Object permissions updated successfully'),
186 category='success')
186 category='success')
187
187
188 except formencode.Invalid as errors:
188 except formencode.Invalid as errors:
189 defaults = errors.value
189 defaults = errors.value
190
190
191 data = render(
191 data = render(
192 'rhodecode:templates/admin/permissions/permissions.mako',
192 'rhodecode:templates/admin/permissions/permissions.mako',
193 self._get_template_context(c), self.request)
193 self._get_template_context(c), self.request)
194 html = formencode.htmlfill.render(
194 html = formencode.htmlfill.render(
195 data,
195 data,
196 defaults=defaults,
196 defaults=defaults,
197 errors=errors.error_dict or {},
197 errors=errors.unpack_errors() or {},
198 prefix_error=False,
198 prefix_error=False,
199 encoding="UTF-8",
199 encoding="UTF-8",
200 force_defaults=False
200 force_defaults=False
201 )
201 )
202 return Response(html)
202 return Response(html)
203 except Exception:
203 except Exception:
204 log.exception("Exception during update of permissions")
204 log.exception("Exception during update of permissions")
205 h.flash(_('Error occurred during update of permissions'),
205 h.flash(_('Error occurred during update of permissions'),
206 category='error')
206 category='error')
207
207
208 affected_user_ids = [User.get_default_user_id()]
208 affected_user_ids = [User.get_default_user_id()]
209 PermissionModel().trigger_permission_flush(affected_user_ids)
209 PermissionModel().trigger_permission_flush(affected_user_ids)
210
210
211 raise HTTPFound(h.route_path('admin_permissions_object'))
211 raise HTTPFound(h.route_path('admin_permissions_object'))
212
212
213 @LoginRequired()
213 @LoginRequired()
214 @HasPermissionAllDecorator('hg.admin')
214 @HasPermissionAllDecorator('hg.admin')
215 def permissions_branch(self):
215 def permissions_branch(self):
216 c = self.load_default_context()
216 c = self.load_default_context()
217 c.active = 'branch'
217 c.active = 'branch'
218
218
219 c.user = User.get_default_user(refresh=True)
219 c.user = User.get_default_user(refresh=True)
220 defaults = {}
220 defaults = {}
221 defaults.update(c.user.get_default_perms())
221 defaults.update(c.user.get_default_perms())
222
222
223 data = render(
223 data = render(
224 'rhodecode:templates/admin/permissions/permissions.mako',
224 'rhodecode:templates/admin/permissions/permissions.mako',
225 self._get_template_context(c), self.request)
225 self._get_template_context(c), self.request)
226 html = formencode.htmlfill.render(
226 html = formencode.htmlfill.render(
227 data,
227 data,
228 defaults=defaults,
228 defaults=defaults,
229 encoding="UTF-8",
229 encoding="UTF-8",
230 force_defaults=False
230 force_defaults=False
231 )
231 )
232 return Response(html)
232 return Response(html)
233
233
234 @LoginRequired()
234 @LoginRequired()
235 @HasPermissionAllDecorator('hg.admin')
235 @HasPermissionAllDecorator('hg.admin')
236 def permissions_global(self):
236 def permissions_global(self):
237 c = self.load_default_context()
237 c = self.load_default_context()
238 c.active = 'global'
238 c.active = 'global'
239
239
240 c.user = User.get_default_user(refresh=True)
240 c.user = User.get_default_user(refresh=True)
241 defaults = {}
241 defaults = {}
242 defaults.update(c.user.get_default_perms())
242 defaults.update(c.user.get_default_perms())
243
243
244 data = render(
244 data = render(
245 'rhodecode:templates/admin/permissions/permissions.mako',
245 'rhodecode:templates/admin/permissions/permissions.mako',
246 self._get_template_context(c), self.request)
246 self._get_template_context(c), self.request)
247 html = formencode.htmlfill.render(
247 html = formencode.htmlfill.render(
248 data,
248 data,
249 defaults=defaults,
249 defaults=defaults,
250 encoding="UTF-8",
250 encoding="UTF-8",
251 force_defaults=False
251 force_defaults=False
252 )
252 )
253 return Response(html)
253 return Response(html)
254
254
255 @LoginRequired()
255 @LoginRequired()
256 @HasPermissionAllDecorator('hg.admin')
256 @HasPermissionAllDecorator('hg.admin')
257 @CSRFRequired()
257 @CSRFRequired()
258 def permissions_global_update(self):
258 def permissions_global_update(self):
259 _ = self.request.translate
259 _ = self.request.translate
260 c = self.load_default_context()
260 c = self.load_default_context()
261 c.active = 'global'
261 c.active = 'global'
262
262
263 _form = UserPermissionsForm(
263 _form = UserPermissionsForm(
264 self.request.translate,
264 self.request.translate,
265 [x[0] for x in c.repo_create_choices],
265 [x[0] for x in c.repo_create_choices],
266 [x[0] for x in c.repo_create_on_write_choices],
266 [x[0] for x in c.repo_create_on_write_choices],
267 [x[0] for x in c.repo_group_create_choices],
267 [x[0] for x in c.repo_group_create_choices],
268 [x[0] for x in c.user_group_create_choices],
268 [x[0] for x in c.user_group_create_choices],
269 [x[0] for x in c.fork_choices],
269 [x[0] for x in c.fork_choices],
270 [x[0] for x in c.inherit_default_permission_choices])()
270 [x[0] for x in c.inherit_default_permission_choices])()
271
271
272 try:
272 try:
273 form_result = _form.to_python(dict(self.request.POST))
273 form_result = _form.to_python(dict(self.request.POST))
274 form_result.update({'perm_user_name': User.DEFAULT_USER})
274 form_result.update({'perm_user_name': User.DEFAULT_USER})
275 PermissionModel().update_user_permissions(form_result)
275 PermissionModel().update_user_permissions(form_result)
276
276
277 Session().commit()
277 Session().commit()
278 h.flash(_('Global permissions updated successfully'),
278 h.flash(_('Global permissions updated successfully'),
279 category='success')
279 category='success')
280
280
281 except formencode.Invalid as errors:
281 except formencode.Invalid as errors:
282 defaults = errors.value
282 defaults = errors.value
283
283
284 data = render(
284 data = render(
285 'rhodecode:templates/admin/permissions/permissions.mako',
285 'rhodecode:templates/admin/permissions/permissions.mako',
286 self._get_template_context(c), self.request)
286 self._get_template_context(c), self.request)
287 html = formencode.htmlfill.render(
287 html = formencode.htmlfill.render(
288 data,
288 data,
289 defaults=defaults,
289 defaults=defaults,
290 errors=errors.error_dict or {},
290 errors=errors.unpack_errors() or {},
291 prefix_error=False,
291 prefix_error=False,
292 encoding="UTF-8",
292 encoding="UTF-8",
293 force_defaults=False
293 force_defaults=False
294 )
294 )
295 return Response(html)
295 return Response(html)
296 except Exception:
296 except Exception:
297 log.exception("Exception during update of permissions")
297 log.exception("Exception during update of permissions")
298 h.flash(_('Error occurred during update of permissions'),
298 h.flash(_('Error occurred during update of permissions'),
299 category='error')
299 category='error')
300
300
301 affected_user_ids = [User.get_default_user_id()]
301 affected_user_ids = [User.get_default_user_id()]
302 PermissionModel().trigger_permission_flush(affected_user_ids)
302 PermissionModel().trigger_permission_flush(affected_user_ids)
303
303
304 raise HTTPFound(h.route_path('admin_permissions_global'))
304 raise HTTPFound(h.route_path('admin_permissions_global'))
305
305
306 @LoginRequired()
306 @LoginRequired()
307 @HasPermissionAllDecorator('hg.admin')
307 @HasPermissionAllDecorator('hg.admin')
308 def permissions_ips(self):
308 def permissions_ips(self):
309 c = self.load_default_context()
309 c = self.load_default_context()
310 c.active = 'ips'
310 c.active = 'ips'
311
311
312 c.user = User.get_default_user(refresh=True)
312 c.user = User.get_default_user(refresh=True)
313 c.user_ip_map = (
313 c.user_ip_map = (
314 UserIpMap.query().filter(UserIpMap.user == c.user).all())
314 UserIpMap.query().filter(UserIpMap.user == c.user).all())
315
315
316 return self._get_template_context(c)
316 return self._get_template_context(c)
317
317
318 @LoginRequired()
318 @LoginRequired()
319 @HasPermissionAllDecorator('hg.admin')
319 @HasPermissionAllDecorator('hg.admin')
320 def permissions_overview(self):
320 def permissions_overview(self):
321 c = self.load_default_context()
321 c = self.load_default_context()
322 c.active = 'perms'
322 c.active = 'perms'
323
323
324 c.user = User.get_default_user(refresh=True)
324 c.user = User.get_default_user(refresh=True)
325 c.perm_user = c.user.AuthUser()
325 c.perm_user = c.user.AuthUser()
326 return self._get_template_context(c)
326 return self._get_template_context(c)
327
327
328 @LoginRequired()
328 @LoginRequired()
329 @HasPermissionAllDecorator('hg.admin')
329 @HasPermissionAllDecorator('hg.admin')
330 def auth_token_access(self):
330 def auth_token_access(self):
331 from rhodecode import CONFIG
331 from rhodecode import CONFIG
332
332
333 c = self.load_default_context()
333 c = self.load_default_context()
334 c.active = 'auth_token_access'
334 c.active = 'auth_token_access'
335
335
336 c.user = User.get_default_user(refresh=True)
336 c.user = User.get_default_user(refresh=True)
337 c.perm_user = c.user.AuthUser()
337 c.perm_user = c.user.AuthUser()
338
338
339 mapper = self.request.registry.queryUtility(IRoutesMapper)
339 mapper = self.request.registry.queryUtility(IRoutesMapper)
340 c.view_data = []
340 c.view_data = []
341
341
342 _argument_prog = re.compile(r'\{(.*?)\}|:\((.*)\)')
342 _argument_prog = re.compile(r'\{(.*?)\}|:\((.*)\)')
343 introspector = self.request.registry.introspector
343 introspector = self.request.registry.introspector
344
344
345 view_intr = {}
345 view_intr = {}
346 for view_data in introspector.get_category('views'):
346 for view_data in introspector.get_category('views'):
347 intr = view_data['introspectable']
347 intr = view_data['introspectable']
348
348
349 if 'route_name' in intr and intr['attr']:
349 if 'route_name' in intr and intr['attr']:
350 view_intr[intr['route_name']] = '{}:{}'.format(
350 view_intr[intr['route_name']] = '{}:{}'.format(
351 str(intr['derived_callable'].__name__), intr['attr']
351 str(intr['derived_callable'].__name__), intr['attr']
352 )
352 )
353
353
354 c.whitelist_key = 'api_access_controllers_whitelist'
354 c.whitelist_key = 'api_access_controllers_whitelist'
355 c.whitelist_file = CONFIG.get('__file__')
355 c.whitelist_file = CONFIG.get('__file__')
356 whitelist_views = aslist(
356 whitelist_views = aslist(
357 CONFIG.get(c.whitelist_key), sep=',')
357 CONFIG.get(c.whitelist_key), sep=',')
358
358
359 for route_info in mapper.get_routes():
359 for route_info in mapper.get_routes():
360 if not route_info.name.startswith('__'):
360 if not route_info.name.startswith('__'):
361 routepath = route_info.pattern
361 routepath = route_info.pattern
362
362
363 def replace(matchobj):
363 def replace(matchobj):
364 if matchobj.group(1):
364 if matchobj.group(1):
365 return "{%s}" % matchobj.group(1).split(':')[0]
365 return "{%s}" % matchobj.group(1).split(':')[0]
366 else:
366 else:
367 return "{%s}" % matchobj.group(2)
367 return "{%s}" % matchobj.group(2)
368
368
369 routepath = _argument_prog.sub(replace, routepath)
369 routepath = _argument_prog.sub(replace, routepath)
370
370
371 if not routepath.startswith('/'):
371 if not routepath.startswith('/'):
372 routepath = '/' + routepath
372 routepath = '/' + routepath
373
373
374 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
374 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
375 active = view_fqn in whitelist_views
375 active = view_fqn in whitelist_views
376 c.view_data.append((route_info.name, view_fqn, routepath, active))
376 c.view_data.append((route_info.name, view_fqn, routepath, active))
377
377
378 c.whitelist_views = whitelist_views
378 c.whitelist_views = whitelist_views
379 return self._get_template_context(c)
379 return self._get_template_context(c)
380
380
381 def ssh_enabled(self):
381 def ssh_enabled(self):
382 return self.request.registry.settings.get(
382 return self.request.registry.settings.get(
383 'ssh.generate_authorized_keyfile')
383 'ssh.generate_authorized_keyfile')
384
384
385 @LoginRequired()
385 @LoginRequired()
386 @HasPermissionAllDecorator('hg.admin')
386 @HasPermissionAllDecorator('hg.admin')
387 def ssh_keys(self):
387 def ssh_keys(self):
388 c = self.load_default_context()
388 c = self.load_default_context()
389 c.active = 'ssh_keys'
389 c.active = 'ssh_keys'
390 c.ssh_enabled = self.ssh_enabled()
390 c.ssh_enabled = self.ssh_enabled()
391 return self._get_template_context(c)
391 return self._get_template_context(c)
392
392
393 @LoginRequired()
393 @LoginRequired()
394 @HasPermissionAllDecorator('hg.admin')
394 @HasPermissionAllDecorator('hg.admin')
395 def ssh_keys_data(self):
395 def ssh_keys_data(self):
396 _ = self.request.translate
396 _ = self.request.translate
397 self.load_default_context()
397 self.load_default_context()
398 column_map = {
398 column_map = {
399 'fingerprint': 'ssh_key_fingerprint',
399 'fingerprint': 'ssh_key_fingerprint',
400 'username': User.username
400 'username': User.username
401 }
401 }
402 draw, start, limit = self._extract_chunk(self.request)
402 draw, start, limit = self._extract_chunk(self.request)
403 search_q, order_by, order_dir = self._extract_ordering(
403 search_q, order_by, order_dir = self._extract_ordering(
404 self.request, column_map=column_map)
404 self.request, column_map=column_map)
405
405
406 ssh_keys_data_total_count = UserSshKeys.query()\
406 ssh_keys_data_total_count = UserSshKeys.query()\
407 .count()
407 .count()
408
408
409 # json generate
409 # json generate
410 base_q = UserSshKeys.query().join(UserSshKeys.user)
410 base_q = UserSshKeys.query().join(UserSshKeys.user)
411
411
412 if search_q:
412 if search_q:
413 like_expression = u'%{}%'.format(safe_unicode(search_q))
413 like_expression = u'%{}%'.format(safe_unicode(search_q))
414 base_q = base_q.filter(or_(
414 base_q = base_q.filter(or_(
415 User.username.ilike(like_expression),
415 User.username.ilike(like_expression),
416 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
416 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
417 ))
417 ))
418
418
419 users_data_total_filtered_count = base_q.count()
419 users_data_total_filtered_count = base_q.count()
420
420
421 sort_col = self._get_order_col(order_by, UserSshKeys)
421 sort_col = self._get_order_col(order_by, UserSshKeys)
422 if sort_col:
422 if sort_col:
423 if order_dir == 'asc':
423 if order_dir == 'asc':
424 # handle null values properly to order by NULL last
424 # handle null values properly to order by NULL last
425 if order_by in ['created_on']:
425 if order_by in ['created_on']:
426 sort_col = coalesce(sort_col, datetime.date.max)
426 sort_col = coalesce(sort_col, datetime.date.max)
427 sort_col = sort_col.asc()
427 sort_col = sort_col.asc()
428 else:
428 else:
429 # handle null values properly to order by NULL last
429 # handle null values properly to order by NULL last
430 if order_by in ['created_on']:
430 if order_by in ['created_on']:
431 sort_col = coalesce(sort_col, datetime.date.min)
431 sort_col = coalesce(sort_col, datetime.date.min)
432 sort_col = sort_col.desc()
432 sort_col = sort_col.desc()
433
433
434 base_q = base_q.order_by(sort_col)
434 base_q = base_q.order_by(sort_col)
435 base_q = base_q.offset(start).limit(limit)
435 base_q = base_q.offset(start).limit(limit)
436
436
437 ssh_keys = base_q.all()
437 ssh_keys = base_q.all()
438
438
439 ssh_keys_data = []
439 ssh_keys_data = []
440 for ssh_key in ssh_keys:
440 for ssh_key in ssh_keys:
441 ssh_keys_data.append({
441 ssh_keys_data.append({
442 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
442 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
443 "fingerprint": ssh_key.ssh_key_fingerprint,
443 "fingerprint": ssh_key.ssh_key_fingerprint,
444 "description": ssh_key.description,
444 "description": ssh_key.description,
445 "created_on": h.format_date(ssh_key.created_on),
445 "created_on": h.format_date(ssh_key.created_on),
446 "accessed_on": h.format_date(ssh_key.accessed_on),
446 "accessed_on": h.format_date(ssh_key.accessed_on),
447 "action": h.link_to(
447 "action": h.link_to(
448 _('Edit'), h.route_path('edit_user_ssh_keys',
448 _('Edit'), h.route_path('edit_user_ssh_keys',
449 user_id=ssh_key.user.user_id))
449 user_id=ssh_key.user.user_id))
450 })
450 })
451
451
452 data = ({
452 data = ({
453 'draw': draw,
453 'draw': draw,
454 'data': ssh_keys_data,
454 'data': ssh_keys_data,
455 'recordsTotal': ssh_keys_data_total_count,
455 'recordsTotal': ssh_keys_data_total_count,
456 'recordsFiltered': users_data_total_filtered_count,
456 'recordsFiltered': users_data_total_filtered_count,
457 })
457 })
458
458
459 return data
459 return data
460
460
461 @LoginRequired()
461 @LoginRequired()
462 @HasPermissionAllDecorator('hg.admin')
462 @HasPermissionAllDecorator('hg.admin')
463 @CSRFRequired()
463 @CSRFRequired()
464 def ssh_keys_update(self):
464 def ssh_keys_update(self):
465 _ = self.request.translate
465 _ = self.request.translate
466 self.load_default_context()
466 self.load_default_context()
467
467
468 ssh_enabled = self.ssh_enabled()
468 ssh_enabled = self.ssh_enabled()
469 key_file = self.request.registry.settings.get(
469 key_file = self.request.registry.settings.get(
470 'ssh.authorized_keys_file_path')
470 'ssh.authorized_keys_file_path')
471 if ssh_enabled:
471 if ssh_enabled:
472 events.trigger(SshKeyFileChangeEvent(), self.request.registry)
472 events.trigger(SshKeyFileChangeEvent(), self.request.registry)
473 h.flash(_('Updated SSH keys file: {}').format(key_file),
473 h.flash(_('Updated SSH keys file: {}').format(key_file),
474 category='success')
474 category='success')
475 else:
475 else:
476 h.flash(_('SSH key support is disabled in .ini file'),
476 h.flash(_('SSH key support is disabled in .ini file'),
477 category='warning')
477 category='warning')
478
478
479 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
479 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -1,356 +1,356 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import datetime
20 import datetime
21 import logging
21 import logging
22 import time
22 import time
23
23
24 import formencode
24 import formencode
25 import formencode.htmlfill
25 import formencode.htmlfill
26
26
27 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
27 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
28
28
29 from pyramid.renderers import render
29 from pyramid.renderers import render
30 from pyramid.response import Response
30 from pyramid.response import Response
31
31
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
34
34
35 from rhodecode.lib.auth import (
35 from rhodecode.lib.auth import (
36 LoginRequired, CSRFRequired, NotAnonymous,
36 LoginRequired, CSRFRequired, NotAnonymous,
37 HasPermissionAny, HasRepoGroupPermissionAny)
37 HasPermissionAny, HasRepoGroupPermissionAny)
38 from rhodecode.lib import helpers as h, audit_logger
38 from rhodecode.lib import helpers as h, audit_logger
39 from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time
39 from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time
40 from rhodecode.model.forms import RepoGroupForm
40 from rhodecode.model.forms import RepoGroupForm
41 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.repo_group import RepoGroupModel
42 from rhodecode.model.repo_group import RepoGroupModel
43 from rhodecode.model.scm import RepoGroupList
43 from rhodecode.model.scm import RepoGroupList
44 from rhodecode.model.db import (
44 from rhodecode.model.db import (
45 or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository)
45 or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository)
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
50 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54
54
55 return c
55 return c
56
56
57 def _load_form_data(self, c):
57 def _load_form_data(self, c):
58 allow_empty_group = False
58 allow_empty_group = False
59
59
60 if self._can_create_repo_group():
60 if self._can_create_repo_group():
61 # we're global admin, we're ok and we can create TOP level groups
61 # we're global admin, we're ok and we can create TOP level groups
62 allow_empty_group = True
62 allow_empty_group = True
63
63
64 # override the choices for this form, we need to filter choices
64 # override the choices for this form, we need to filter choices
65 # and display only those we have ADMIN right
65 # and display only those we have ADMIN right
66 groups_with_admin_rights = RepoGroupList(
66 groups_with_admin_rights = RepoGroupList(
67 RepoGroup.query().all(),
67 RepoGroup.query().all(),
68 perm_set=['group.admin'], extra_kwargs=dict(user=self._rhodecode_user))
68 perm_set=['group.admin'], extra_kwargs=dict(user=self._rhodecode_user))
69 c.repo_groups = RepoGroup.groups_choices(
69 c.repo_groups = RepoGroup.groups_choices(
70 groups=groups_with_admin_rights,
70 groups=groups_with_admin_rights,
71 show_empty_group=allow_empty_group)
71 show_empty_group=allow_empty_group)
72 c.personal_repo_group = self._rhodecode_user.personal_repo_group
72 c.personal_repo_group = self._rhodecode_user.personal_repo_group
73
73
74 def _can_create_repo_group(self, parent_group_id=None):
74 def _can_create_repo_group(self, parent_group_id=None):
75 is_admin = HasPermissionAny('hg.admin')('group create controller')
75 is_admin = HasPermissionAny('hg.admin')('group create controller')
76 create_repo_group = HasPermissionAny(
76 create_repo_group = HasPermissionAny(
77 'hg.repogroup.create.true')('group create controller')
77 'hg.repogroup.create.true')('group create controller')
78 if is_admin or (create_repo_group and not parent_group_id):
78 if is_admin or (create_repo_group and not parent_group_id):
79 # we're global admin, or we have global repo group create
79 # we're global admin, or we have global repo group create
80 # permission
80 # permission
81 # we're ok and we can create TOP level groups
81 # we're ok and we can create TOP level groups
82 return True
82 return True
83 elif parent_group_id:
83 elif parent_group_id:
84 # we check the permission if we can write to parent group
84 # we check the permission if we can write to parent group
85 group = RepoGroup.get(parent_group_id)
85 group = RepoGroup.get(parent_group_id)
86 group_name = group.group_name if group else None
86 group_name = group.group_name if group else None
87 if HasRepoGroupPermissionAny('group.admin')(
87 if HasRepoGroupPermissionAny('group.admin')(
88 group_name, 'check if user is an admin of group'):
88 group_name, 'check if user is an admin of group'):
89 # we're an admin of passed in group, we're ok.
89 # we're an admin of passed in group, we're ok.
90 return True
90 return True
91 else:
91 else:
92 return False
92 return False
93 return False
93 return False
94
94
95 # permission check in data loading of
95 # permission check in data loading of
96 # `repo_group_list_data` via RepoGroupList
96 # `repo_group_list_data` via RepoGroupList
97 @LoginRequired()
97 @LoginRequired()
98 @NotAnonymous()
98 @NotAnonymous()
99 def repo_group_list(self):
99 def repo_group_list(self):
100 c = self.load_default_context()
100 c = self.load_default_context()
101 return self._get_template_context(c)
101 return self._get_template_context(c)
102
102
103 # permission check inside
103 # permission check inside
104 @LoginRequired()
104 @LoginRequired()
105 @NotAnonymous()
105 @NotAnonymous()
106 def repo_group_list_data(self):
106 def repo_group_list_data(self):
107 self.load_default_context()
107 self.load_default_context()
108 column_map = {
108 column_map = {
109 'name': 'group_name_hash',
109 'name': 'group_name_hash',
110 'desc': 'group_description',
110 'desc': 'group_description',
111 'last_change': 'updated_on',
111 'last_change': 'updated_on',
112 'top_level_repos': 'repos_total',
112 'top_level_repos': 'repos_total',
113 'owner': 'user_username',
113 'owner': 'user_username',
114 }
114 }
115 draw, start, limit = self._extract_chunk(self.request)
115 draw, start, limit = self._extract_chunk(self.request)
116 search_q, order_by, order_dir = self._extract_ordering(
116 search_q, order_by, order_dir = self._extract_ordering(
117 self.request, column_map=column_map)
117 self.request, column_map=column_map)
118
118
119 _render = self.request.get_partial_renderer(
119 _render = self.request.get_partial_renderer(
120 'rhodecode:templates/data_table/_dt_elements.mako')
120 'rhodecode:templates/data_table/_dt_elements.mako')
121 c = _render.get_call_context()
121 c = _render.get_call_context()
122
122
123 def quick_menu(repo_group_name):
123 def quick_menu(repo_group_name):
124 return _render('quick_repo_group_menu', repo_group_name)
124 return _render('quick_repo_group_menu', repo_group_name)
125
125
126 def repo_group_lnk(repo_group_name):
126 def repo_group_lnk(repo_group_name):
127 return _render('repo_group_name', repo_group_name)
127 return _render('repo_group_name', repo_group_name)
128
128
129 def last_change(last_change):
129 def last_change(last_change):
130 if isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
130 if isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
131 ts = time.time()
131 ts = time.time()
132 utc_offset = (datetime.datetime.fromtimestamp(ts)
132 utc_offset = (datetime.datetime.fromtimestamp(ts)
133 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
133 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
134 last_change = last_change + datetime.timedelta(seconds=utc_offset)
134 last_change = last_change + datetime.timedelta(seconds=utc_offset)
135 return _render("last_change", last_change)
135 return _render("last_change", last_change)
136
136
137 def desc(desc, personal):
137 def desc(desc, personal):
138 return _render(
138 return _render(
139 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
139 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
140
140
141 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
141 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
142 return _render(
142 return _render(
143 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
143 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
144
144
145 def user_profile(username):
145 def user_profile(username):
146 return _render('user_profile', username)
146 return _render('user_profile', username)
147
147
148 _perms = ['group.admin']
148 _perms = ['group.admin']
149 allowed_ids = [-1] + self._rhodecode_user.repo_group_acl_ids_from_stack(_perms)
149 allowed_ids = [-1] + self._rhodecode_user.repo_group_acl_ids_from_stack(_perms)
150
150
151 repo_groups_data_total_count = RepoGroup.query()\
151 repo_groups_data_total_count = RepoGroup.query()\
152 .filter(or_(
152 .filter(or_(
153 # generate multiple IN to fix limitation problems
153 # generate multiple IN to fix limitation problems
154 *in_filter_generator(RepoGroup.group_id, allowed_ids)
154 *in_filter_generator(RepoGroup.group_id, allowed_ids)
155 )) \
155 )) \
156 .count()
156 .count()
157
157
158 repo_groups_data_total_inactive_count = RepoGroup.query()\
158 repo_groups_data_total_inactive_count = RepoGroup.query()\
159 .filter(RepoGroup.group_id.in_(allowed_ids))\
159 .filter(RepoGroup.group_id.in_(allowed_ids))\
160 .count()
160 .count()
161
161
162 repo_count = count(Repository.repo_id)
162 repo_count = count(Repository.repo_id)
163 base_q = Session.query(
163 base_q = Session.query(
164 RepoGroup.group_name,
164 RepoGroup.group_name,
165 RepoGroup.group_name_hash,
165 RepoGroup.group_name_hash,
166 RepoGroup.group_description,
166 RepoGroup.group_description,
167 RepoGroup.group_id,
167 RepoGroup.group_id,
168 RepoGroup.personal,
168 RepoGroup.personal,
169 RepoGroup.updated_on,
169 RepoGroup.updated_on,
170 User,
170 User,
171 repo_count.label('repos_count')
171 repo_count.label('repos_count')
172 ) \
172 ) \
173 .filter(or_(
173 .filter(or_(
174 # generate multiple IN to fix limitation problems
174 # generate multiple IN to fix limitation problems
175 *in_filter_generator(RepoGroup.group_id, allowed_ids)
175 *in_filter_generator(RepoGroup.group_id, allowed_ids)
176 )) \
176 )) \
177 .outerjoin(Repository, Repository.group_id == RepoGroup.group_id) \
177 .outerjoin(Repository, Repository.group_id == RepoGroup.group_id) \
178 .join(User, User.user_id == RepoGroup.user_id) \
178 .join(User, User.user_id == RepoGroup.user_id) \
179 .group_by(RepoGroup, User)
179 .group_by(RepoGroup, User)
180
180
181 if search_q:
181 if search_q:
182 like_expression = u'%{}%'.format(safe_unicode(search_q))
182 like_expression = u'%{}%'.format(safe_unicode(search_q))
183 base_q = base_q.filter(or_(
183 base_q = base_q.filter(or_(
184 RepoGroup.group_name.ilike(like_expression),
184 RepoGroup.group_name.ilike(like_expression),
185 ))
185 ))
186
186
187 repo_groups_data_total_filtered_count = base_q.count()
187 repo_groups_data_total_filtered_count = base_q.count()
188 # the inactive isn't really used, but we still make it same as other data grids
188 # the inactive isn't really used, but we still make it same as other data grids
189 # which use inactive (users,user groups)
189 # which use inactive (users,user groups)
190 repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count
190 repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count
191
191
192 sort_defined = False
192 sort_defined = False
193 if order_by == 'group_name':
193 if order_by == 'group_name':
194 sort_col = func.lower(RepoGroup.group_name)
194 sort_col = func.lower(RepoGroup.group_name)
195 sort_defined = True
195 sort_defined = True
196 elif order_by == 'repos_total':
196 elif order_by == 'repos_total':
197 sort_col = repo_count
197 sort_col = repo_count
198 sort_defined = True
198 sort_defined = True
199 elif order_by == 'user_username':
199 elif order_by == 'user_username':
200 sort_col = User.username
200 sort_col = User.username
201 else:
201 else:
202 sort_col = getattr(RepoGroup, order_by, None)
202 sort_col = getattr(RepoGroup, order_by, None)
203
203
204 if sort_defined or sort_col:
204 if sort_defined or sort_col:
205 if order_dir == 'asc':
205 if order_dir == 'asc':
206 sort_col = sort_col.asc()
206 sort_col = sort_col.asc()
207 else:
207 else:
208 sort_col = sort_col.desc()
208 sort_col = sort_col.desc()
209
209
210 base_q = base_q.order_by(sort_col)
210 base_q = base_q.order_by(sort_col)
211 base_q = base_q.offset(start).limit(limit)
211 base_q = base_q.offset(start).limit(limit)
212
212
213 # authenticated access to user groups
213 # authenticated access to user groups
214 auth_repo_group_list = base_q.all()
214 auth_repo_group_list = base_q.all()
215
215
216 repo_groups_data = []
216 repo_groups_data = []
217 for repo_gr in auth_repo_group_list:
217 for repo_gr in auth_repo_group_list:
218 row = {
218 row = {
219 "menu": quick_menu(repo_gr.group_name),
219 "menu": quick_menu(repo_gr.group_name),
220 "name": repo_group_lnk(repo_gr.group_name),
220 "name": repo_group_lnk(repo_gr.group_name),
221
221
222 "last_change": last_change(repo_gr.updated_on),
222 "last_change": last_change(repo_gr.updated_on),
223
223
224 "last_changeset": "",
224 "last_changeset": "",
225 "last_changeset_raw": "",
225 "last_changeset_raw": "",
226
226
227 "desc": desc(repo_gr.group_description, repo_gr.personal),
227 "desc": desc(repo_gr.group_description, repo_gr.personal),
228 "owner": user_profile(repo_gr.User.username),
228 "owner": user_profile(repo_gr.User.username),
229 "top_level_repos": repo_gr.repos_count,
229 "top_level_repos": repo_gr.repos_count,
230 "action": repo_group_actions(
230 "action": repo_group_actions(
231 repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count),
231 repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count),
232
232
233 }
233 }
234
234
235 repo_groups_data.append(row)
235 repo_groups_data.append(row)
236
236
237 data = ({
237 data = ({
238 'draw': draw,
238 'draw': draw,
239 'data': repo_groups_data,
239 'data': repo_groups_data,
240 'recordsTotal': repo_groups_data_total_count,
240 'recordsTotal': repo_groups_data_total_count,
241 'recordsTotalInactive': repo_groups_data_total_inactive_count,
241 'recordsTotalInactive': repo_groups_data_total_inactive_count,
242 'recordsFiltered': repo_groups_data_total_filtered_count,
242 'recordsFiltered': repo_groups_data_total_filtered_count,
243 'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count,
243 'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count,
244 })
244 })
245
245
246 return data
246 return data
247
247
248 @LoginRequired()
248 @LoginRequired()
249 @NotAnonymous()
249 @NotAnonymous()
250 # perm checks inside
250 # perm checks inside
251 def repo_group_new(self):
251 def repo_group_new(self):
252 c = self.load_default_context()
252 c = self.load_default_context()
253
253
254 # perm check for admin, create_group perm or admin of parent_group
254 # perm check for admin, create_group perm or admin of parent_group
255 parent_group_id = safe_int(self.request.GET.get('parent_group'))
255 parent_group_id = safe_int(self.request.GET.get('parent_group'))
256 _gr = RepoGroup.get(parent_group_id)
256 _gr = RepoGroup.get(parent_group_id)
257 if not self._can_create_repo_group(parent_group_id):
257 if not self._can_create_repo_group(parent_group_id):
258 raise HTTPForbidden()
258 raise HTTPForbidden()
259
259
260 self._load_form_data(c)
260 self._load_form_data(c)
261
261
262 defaults = {} # Future proof for default of repo group
262 defaults = {} # Future proof for default of repo group
263
263
264 parent_group_choice = '-1'
264 parent_group_choice = '-1'
265 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
265 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
266 parent_group_choice = self._rhodecode_user.personal_repo_group
266 parent_group_choice = self._rhodecode_user.personal_repo_group
267
267
268 if parent_group_id and _gr:
268 if parent_group_id and _gr:
269 if parent_group_id in [x[0] for x in c.repo_groups]:
269 if parent_group_id in [x[0] for x in c.repo_groups]:
270 parent_group_choice = safe_unicode(parent_group_id)
270 parent_group_choice = safe_unicode(parent_group_id)
271
271
272 defaults.update({'group_parent_id': parent_group_choice})
272 defaults.update({'group_parent_id': parent_group_choice})
273
273
274 data = render(
274 data = render(
275 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
275 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
276 self._get_template_context(c), self.request)
276 self._get_template_context(c), self.request)
277
277
278 html = formencode.htmlfill.render(
278 html = formencode.htmlfill.render(
279 data,
279 data,
280 defaults=defaults,
280 defaults=defaults,
281 encoding="UTF-8",
281 encoding="UTF-8",
282 force_defaults=False
282 force_defaults=False
283 )
283 )
284 return Response(html)
284 return Response(html)
285
285
286 @LoginRequired()
286 @LoginRequired()
287 @NotAnonymous()
287 @NotAnonymous()
288 @CSRFRequired()
288 @CSRFRequired()
289 # perm checks inside
289 # perm checks inside
290 def repo_group_create(self):
290 def repo_group_create(self):
291 c = self.load_default_context()
291 c = self.load_default_context()
292 _ = self.request.translate
292 _ = self.request.translate
293
293
294 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
294 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
295 can_create = self._can_create_repo_group(parent_group_id)
295 can_create = self._can_create_repo_group(parent_group_id)
296
296
297 self._load_form_data(c)
297 self._load_form_data(c)
298 # permissions for can create group based on parent_id are checked
298 # permissions for can create group based on parent_id are checked
299 # here in the Form
299 # here in the Form
300 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
300 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
301 repo_group_form = RepoGroupForm(
301 repo_group_form = RepoGroupForm(
302 self.request.translate, available_groups=available_groups,
302 self.request.translate, available_groups=available_groups,
303 can_create_in_root=can_create)()
303 can_create_in_root=can_create)()
304
304
305 repo_group_name = self.request.POST.get('group_name')
305 repo_group_name = self.request.POST.get('group_name')
306 try:
306 try:
307 owner = self._rhodecode_user
307 owner = self._rhodecode_user
308 form_result = repo_group_form.to_python(dict(self.request.POST))
308 form_result = repo_group_form.to_python(dict(self.request.POST))
309 copy_permissions = form_result.get('group_copy_permissions')
309 copy_permissions = form_result.get('group_copy_permissions')
310 repo_group = RepoGroupModel().create(
310 repo_group = RepoGroupModel().create(
311 group_name=form_result['group_name_full'],
311 group_name=form_result['group_name_full'],
312 group_description=form_result['group_description'],
312 group_description=form_result['group_description'],
313 owner=owner.user_id,
313 owner=owner.user_id,
314 copy_permissions=form_result['group_copy_permissions']
314 copy_permissions=form_result['group_copy_permissions']
315 )
315 )
316 Session().flush()
316 Session().flush()
317
317
318 repo_group_data = repo_group.get_api_data()
318 repo_group_data = repo_group.get_api_data()
319 audit_logger.store_web(
319 audit_logger.store_web(
320 'repo_group.create', action_data={'data': repo_group_data},
320 'repo_group.create', action_data={'data': repo_group_data},
321 user=self._rhodecode_user)
321 user=self._rhodecode_user)
322
322
323 Session().commit()
323 Session().commit()
324
324
325 _new_group_name = form_result['group_name_full']
325 _new_group_name = form_result['group_name_full']
326
326
327 repo_group_url = h.link_to(
327 repo_group_url = h.link_to(
328 _new_group_name,
328 _new_group_name,
329 h.route_path('repo_group_home', repo_group_name=_new_group_name))
329 h.route_path('repo_group_home', repo_group_name=_new_group_name))
330 h.flash(h.literal(_('Created repository group %s')
330 h.flash(h.literal(_('Created repository group %s')
331 % repo_group_url), category='success')
331 % repo_group_url), category='success')
332
332
333 except formencode.Invalid as errors:
333 except formencode.Invalid as errors:
334 data = render(
334 data = render(
335 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
335 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
336 self._get_template_context(c), self.request)
336 self._get_template_context(c), self.request)
337 html = formencode.htmlfill.render(
337 html = formencode.htmlfill.render(
338 data,
338 data,
339 defaults=errors.value,
339 defaults=errors.value,
340 errors=errors.error_dict or {},
340 errors=errors.unpack_errors() or {},
341 prefix_error=False,
341 prefix_error=False,
342 encoding="UTF-8",
342 encoding="UTF-8",
343 force_defaults=False
343 force_defaults=False
344 )
344 )
345 return Response(html)
345 return Response(html)
346 except Exception:
346 except Exception:
347 log.exception("Exception during creation of repository group")
347 log.exception("Exception during creation of repository group")
348 h.flash(_('Error occurred during creation of repository group %s')
348 h.flash(_('Error occurred during creation of repository group %s')
349 % repo_group_name, category='error')
349 % repo_group_name, category='error')
350 raise HTTPFound(h.route_path('home'))
350 raise HTTPFound(h.route_path('home'))
351
351
352 PermissionModel().trigger_permission_flush()
352 PermissionModel().trigger_permission_flush()
353
353
354 raise HTTPFound(
354 raise HTTPFound(
355 h.route_path('repo_group_home',
355 h.route_path('repo_group_home',
356 repo_group_name=form_result['group_name_full']))
356 repo_group_name=form_result['group_name_full']))
@@ -1,250 +1,250 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import formencode
22 import formencode
23 import formencode.htmlfill
23 import formencode.htmlfill
24
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26
26
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 import events
30 from rhodecode import events
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.celerylib.utils import get_task_id
32 from rhodecode.lib.celerylib.utils import get_task_id
33
33
34 from rhodecode.lib.auth import (
34 from rhodecode.lib.auth import (
35 LoginRequired, CSRFRequired, NotAnonymous,
35 LoginRequired, CSRFRequired, NotAnonymous,
36 HasPermissionAny, HasRepoGroupPermissionAny)
36 HasPermissionAny, HasRepoGroupPermissionAny)
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.utils import repo_name_slug
38 from rhodecode.lib.utils import repo_name_slug
39 from rhodecode.lib.utils2 import safe_int, safe_unicode
39 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 from rhodecode.model.forms import RepoForm
40 from rhodecode.model.forms import RepoForm
41 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.db import (
45 from rhodecode.model.db import (
46 in_filter_generator, or_, func, Session, Repository, RepoGroup, User)
46 in_filter_generator, or_, func, Session, Repository, RepoGroup, User)
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class AdminReposView(BaseAppView, DataGridAppView):
51 class AdminReposView(BaseAppView, DataGridAppView):
52
52
53 def load_default_context(self):
53 def load_default_context(self):
54 c = self._get_local_tmpl_context()
54 c = self._get_local_tmpl_context()
55 return c
55 return c
56
56
57 def _load_form_data(self, c):
57 def _load_form_data(self, c):
58 acl_groups = RepoGroupList(RepoGroup.query().all(),
58 acl_groups = RepoGroupList(RepoGroup.query().all(),
59 perm_set=['group.write', 'group.admin'])
59 perm_set=['group.write', 'group.admin'])
60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
61 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
61 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
62 c.personal_repo_group = self._rhodecode_user.personal_repo_group
62 c.personal_repo_group = self._rhodecode_user.personal_repo_group
63
63
64 @LoginRequired()
64 @LoginRequired()
65 @NotAnonymous()
65 @NotAnonymous()
66 # perms check inside
66 # perms check inside
67 def repository_list(self):
67 def repository_list(self):
68 c = self.load_default_context()
68 c = self.load_default_context()
69 return self._get_template_context(c)
69 return self._get_template_context(c)
70
70
71 @LoginRequired()
71 @LoginRequired()
72 @NotAnonymous()
72 @NotAnonymous()
73 # perms check inside
73 # perms check inside
74 def repository_list_data(self):
74 def repository_list_data(self):
75 self.load_default_context()
75 self.load_default_context()
76 column_map = {
76 column_map = {
77 'name': 'repo_name',
77 'name': 'repo_name',
78 'desc': 'description',
78 'desc': 'description',
79 'last_change': 'updated_on',
79 'last_change': 'updated_on',
80 'owner': 'user_username',
80 'owner': 'user_username',
81 }
81 }
82 draw, start, limit = self._extract_chunk(self.request)
82 draw, start, limit = self._extract_chunk(self.request)
83 search_q, order_by, order_dir = self._extract_ordering(
83 search_q, order_by, order_dir = self._extract_ordering(
84 self.request, column_map=column_map)
84 self.request, column_map=column_map)
85
85
86 _perms = ['repository.admin']
86 _perms = ['repository.admin']
87 allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(_perms)
87 allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(_perms)
88
88
89 repos_data_total_count = Repository.query() \
89 repos_data_total_count = Repository.query() \
90 .filter(or_(
90 .filter(or_(
91 # generate multiple IN to fix limitation problems
91 # generate multiple IN to fix limitation problems
92 *in_filter_generator(Repository.repo_id, allowed_ids))
92 *in_filter_generator(Repository.repo_id, allowed_ids))
93 ) \
93 ) \
94 .count()
94 .count()
95
95
96 base_q = Session.query(
96 base_q = Session.query(
97 Repository.repo_id,
97 Repository.repo_id,
98 Repository.repo_name,
98 Repository.repo_name,
99 Repository.description,
99 Repository.description,
100 Repository.repo_type,
100 Repository.repo_type,
101 Repository.repo_state,
101 Repository.repo_state,
102 Repository.private,
102 Repository.private,
103 Repository.archived,
103 Repository.archived,
104 Repository.fork,
104 Repository.fork,
105 Repository.updated_on,
105 Repository.updated_on,
106 Repository._changeset_cache,
106 Repository._changeset_cache,
107 User,
107 User,
108 ) \
108 ) \
109 .filter(or_(
109 .filter(or_(
110 # generate multiple IN to fix limitation problems
110 # generate multiple IN to fix limitation problems
111 *in_filter_generator(Repository.repo_id, allowed_ids))
111 *in_filter_generator(Repository.repo_id, allowed_ids))
112 ) \
112 ) \
113 .join(User, User.user_id == Repository.user_id) \
113 .join(User, User.user_id == Repository.user_id) \
114 .group_by(Repository, User)
114 .group_by(Repository, User)
115
115
116 if search_q:
116 if search_q:
117 like_expression = u'%{}%'.format(safe_unicode(search_q))
117 like_expression = u'%{}%'.format(safe_unicode(search_q))
118 base_q = base_q.filter(or_(
118 base_q = base_q.filter(or_(
119 Repository.repo_name.ilike(like_expression),
119 Repository.repo_name.ilike(like_expression),
120 ))
120 ))
121
121
122 repos_data_total_filtered_count = base_q.count()
122 repos_data_total_filtered_count = base_q.count()
123
123
124 sort_defined = False
124 sort_defined = False
125 if order_by == 'repo_name':
125 if order_by == 'repo_name':
126 sort_col = func.lower(Repository.repo_name)
126 sort_col = func.lower(Repository.repo_name)
127 sort_defined = True
127 sort_defined = True
128 elif order_by == 'user_username':
128 elif order_by == 'user_username':
129 sort_col = User.username
129 sort_col = User.username
130 else:
130 else:
131 sort_col = getattr(Repository, order_by, None)
131 sort_col = getattr(Repository, order_by, None)
132
132
133 if sort_defined or sort_col:
133 if sort_defined or sort_col:
134 if order_dir == 'asc':
134 if order_dir == 'asc':
135 sort_col = sort_col.asc()
135 sort_col = sort_col.asc()
136 else:
136 else:
137 sort_col = sort_col.desc()
137 sort_col = sort_col.desc()
138
138
139 base_q = base_q.order_by(sort_col)
139 base_q = base_q.order_by(sort_col)
140 base_q = base_q.offset(start).limit(limit)
140 base_q = base_q.offset(start).limit(limit)
141
141
142 repos_list = base_q.all()
142 repos_list = base_q.all()
143
143
144 repos_data = RepoModel().get_repos_as_dict(
144 repos_data = RepoModel().get_repos_as_dict(
145 repo_list=repos_list, admin=True, super_user_actions=True)
145 repo_list=repos_list, admin=True, super_user_actions=True)
146
146
147 data = ({
147 data = ({
148 'draw': draw,
148 'draw': draw,
149 'data': repos_data,
149 'data': repos_data,
150 'recordsTotal': repos_data_total_count,
150 'recordsTotal': repos_data_total_count,
151 'recordsFiltered': repos_data_total_filtered_count,
151 'recordsFiltered': repos_data_total_filtered_count,
152 })
152 })
153 return data
153 return data
154
154
155 @LoginRequired()
155 @LoginRequired()
156 @NotAnonymous()
156 @NotAnonymous()
157 # perms check inside
157 # perms check inside
158 def repository_new(self):
158 def repository_new(self):
159 c = self.load_default_context()
159 c = self.load_default_context()
160
160
161 new_repo = self.request.GET.get('repo', '')
161 new_repo = self.request.GET.get('repo', '')
162 parent_group_id = safe_int(self.request.GET.get('parent_group'))
162 parent_group_id = safe_int(self.request.GET.get('parent_group'))
163 _gr = RepoGroup.get(parent_group_id)
163 _gr = RepoGroup.get(parent_group_id)
164
164
165 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
165 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
166 # you're not super admin nor have global create permissions,
166 # you're not super admin nor have global create permissions,
167 # but maybe you have at least write permission to a parent group ?
167 # but maybe you have at least write permission to a parent group ?
168
168
169 gr_name = _gr.group_name if _gr else None
169 gr_name = _gr.group_name if _gr else None
170 # create repositories with write permission on group is set to true
170 # create repositories with write permission on group is set to true
171 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
171 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
172 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
172 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
173 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
173 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
174 if not (group_admin or (group_write and create_on_write)):
174 if not (group_admin or (group_write and create_on_write)):
175 raise HTTPForbidden()
175 raise HTTPForbidden()
176
176
177 self._load_form_data(c)
177 self._load_form_data(c)
178 c.new_repo = repo_name_slug(new_repo)
178 c.new_repo = repo_name_slug(new_repo)
179
179
180 # apply the defaults from defaults page
180 # apply the defaults from defaults page
181 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
181 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
182 # set checkbox to autochecked
182 # set checkbox to autochecked
183 defaults['repo_copy_permissions'] = True
183 defaults['repo_copy_permissions'] = True
184
184
185 parent_group_choice = '-1'
185 parent_group_choice = '-1'
186 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
186 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
187 parent_group_choice = self._rhodecode_user.personal_repo_group
187 parent_group_choice = self._rhodecode_user.personal_repo_group
188
188
189 if parent_group_id and _gr:
189 if parent_group_id and _gr:
190 if parent_group_id in [x[0] for x in c.repo_groups]:
190 if parent_group_id in [x[0] for x in c.repo_groups]:
191 parent_group_choice = safe_unicode(parent_group_id)
191 parent_group_choice = safe_unicode(parent_group_id)
192
192
193 defaults.update({'repo_group': parent_group_choice})
193 defaults.update({'repo_group': parent_group_choice})
194
194
195 data = render('rhodecode:templates/admin/repos/repo_add.mako',
195 data = render('rhodecode:templates/admin/repos/repo_add.mako',
196 self._get_template_context(c), self.request)
196 self._get_template_context(c), self.request)
197 html = formencode.htmlfill.render(
197 html = formencode.htmlfill.render(
198 data,
198 data,
199 defaults=defaults,
199 defaults=defaults,
200 encoding="UTF-8",
200 encoding="UTF-8",
201 force_defaults=False
201 force_defaults=False
202 )
202 )
203 return Response(html)
203 return Response(html)
204
204
205 @LoginRequired()
205 @LoginRequired()
206 @NotAnonymous()
206 @NotAnonymous()
207 @CSRFRequired()
207 @CSRFRequired()
208 # perms check inside
208 # perms check inside
209 def repository_create(self):
209 def repository_create(self):
210 c = self.load_default_context()
210 c = self.load_default_context()
211
211
212 form_result = {}
212 form_result = {}
213 self._load_form_data(c)
213 self._load_form_data(c)
214
214
215 try:
215 try:
216 # CanWriteToGroup validators checks permissions of this POST
216 # CanWriteToGroup validators checks permissions of this POST
217 form = RepoForm(
217 form = RepoForm(
218 self.request.translate, repo_groups=c.repo_groups_choices)()
218 self.request.translate, repo_groups=c.repo_groups_choices)()
219 form_result = form.to_python(dict(self.request.POST))
219 form_result = form.to_python(dict(self.request.POST))
220 copy_permissions = form_result.get('repo_copy_permissions')
220 copy_permissions = form_result.get('repo_copy_permissions')
221 # create is done sometimes async on celery, db transaction
221 # create is done sometimes async on celery, db transaction
222 # management is handled there.
222 # management is handled there.
223 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
223 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
224 task_id = get_task_id(task)
224 task_id = get_task_id(task)
225 except formencode.Invalid as errors:
225 except formencode.Invalid as errors:
226 data = render('rhodecode:templates/admin/repos/repo_add.mako',
226 data = render('rhodecode:templates/admin/repos/repo_add.mako',
227 self._get_template_context(c), self.request)
227 self._get_template_context(c), self.request)
228 html = formencode.htmlfill.render(
228 html = formencode.htmlfill.render(
229 data,
229 data,
230 defaults=errors.value,
230 defaults=errors.value,
231 errors=errors.error_dict or {},
231 errors=errors.unpack_errors() or {},
232 prefix_error=False,
232 prefix_error=False,
233 encoding="UTF-8",
233 encoding="UTF-8",
234 force_defaults=False
234 force_defaults=False
235 )
235 )
236 return Response(html)
236 return Response(html)
237
237
238 except Exception as e:
238 except Exception as e:
239 msg = self._log_creation_exception(e, form_result.get('repo_name'))
239 msg = self._log_creation_exception(e, form_result.get('repo_name'))
240 h.flash(msg, category='error')
240 h.flash(msg, category='error')
241 raise HTTPFound(h.route_path('home'))
241 raise HTTPFound(h.route_path('home'))
242
242
243 repo_name = form_result.get('repo_name_full')
243 repo_name = form_result.get('repo_name_full')
244
244
245 affected_user_ids = [self._rhodecode_user.user_id]
245 affected_user_ids = [self._rhodecode_user.user_id]
246 PermissionModel().trigger_permission_flush(affected_user_ids)
246 PermissionModel().trigger_permission_flush(affected_user_ids)
247
247
248 raise HTTPFound(
248 raise HTTPFound(
249 h.route_path('repo_creating', repo_name=repo_name,
249 h.route_path('repo_creating', repo_name=repo_name,
250 _query=dict(task_id=task_id)))
250 _query=dict(task_id=task_id)))
@@ -1,720 +1,720 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 import datetime
25 import datetime
26 import formencode
26 import formencode
27 import formencode.htmlfill
27 import formencode.htmlfill
28
28
29 import rhodecode
29 import rhodecode
30
30
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
32 from pyramid.renderers import render
32 from pyramid.renderers import render
33 from pyramid.response import Response
33 from pyramid.response import Response
34
34
35 from rhodecode.apps._base import BaseAppView
35 from rhodecode.apps._base import BaseAppView
36 from rhodecode.apps._base.navigation import navigation_list
36 from rhodecode.apps._base.navigation import navigation_list
37 from rhodecode.apps.svn_support.config_keys import generate_config
37 from rhodecode.apps.svn_support.config_keys import generate_config
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import (
39 from rhodecode.lib.auth import (
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
41 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.utils import repo2db_mapper
42 from rhodecode.lib.utils import repo2db_mapper
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
44 from rhodecode.lib.index import searcher_from_config
44 from rhodecode.lib.index import searcher_from_config
45
45
46 from rhodecode.model.db import RhodeCodeUi, Repository
46 from rhodecode.model.db import RhodeCodeUi, Repository
47 from rhodecode.model.forms import (ApplicationSettingsForm,
47 from rhodecode.model.forms import (ApplicationSettingsForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
49 LabsSettingsForm, IssueTrackerPatternsForm)
49 LabsSettingsForm, IssueTrackerPatternsForm)
50 from rhodecode.model.permission import PermissionModel
50 from rhodecode.model.permission import PermissionModel
51 from rhodecode.model.repo_group import RepoGroupModel
51 from rhodecode.model.repo_group import RepoGroupModel
52
52
53 from rhodecode.model.scm import ScmModel
53 from rhodecode.model.scm import ScmModel
54 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.notification import EmailNotificationModel
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.model.settings import (
56 from rhodecode.model.settings import (
57 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
57 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
58 SettingsModel)
58 SettingsModel)
59
59
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63
63
64 class AdminSettingsView(BaseAppView):
64 class AdminSettingsView(BaseAppView):
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 c.labs_active = str2bool(
68 c.labs_active = str2bool(
69 rhodecode.CONFIG.get('labs_settings_active', 'true'))
69 rhodecode.CONFIG.get('labs_settings_active', 'true'))
70 c.navlist = navigation_list(self.request)
70 c.navlist = navigation_list(self.request)
71 return c
71 return c
72
72
73 @classmethod
73 @classmethod
74 def _get_ui_settings(cls):
74 def _get_ui_settings(cls):
75 ret = RhodeCodeUi.query().all()
75 ret = RhodeCodeUi.query().all()
76
76
77 if not ret:
77 if not ret:
78 raise Exception('Could not get application ui settings !')
78 raise Exception('Could not get application ui settings !')
79 settings = {}
79 settings = {}
80 for each in ret:
80 for each in ret:
81 k = each.ui_key
81 k = each.ui_key
82 v = each.ui_value
82 v = each.ui_value
83 if k == '/':
83 if k == '/':
84 k = 'root_path'
84 k = 'root_path'
85
85
86 if k in ['push_ssl', 'publish', 'enabled']:
86 if k in ['push_ssl', 'publish', 'enabled']:
87 v = str2bool(v)
87 v = str2bool(v)
88
88
89 if k.find('.') != -1:
89 if k.find('.') != -1:
90 k = k.replace('.', '_')
90 k = k.replace('.', '_')
91
91
92 if each.ui_section in ['hooks', 'extensions']:
92 if each.ui_section in ['hooks', 'extensions']:
93 v = each.ui_active
93 v = each.ui_active
94
94
95 settings[each.ui_section + '_' + k] = v
95 settings[each.ui_section + '_' + k] = v
96 return settings
96 return settings
97
97
98 @classmethod
98 @classmethod
99 def _form_defaults(cls):
99 def _form_defaults(cls):
100 defaults = SettingsModel().get_all_settings()
100 defaults = SettingsModel().get_all_settings()
101 defaults.update(cls._get_ui_settings())
101 defaults.update(cls._get_ui_settings())
102
102
103 defaults.update({
103 defaults.update({
104 'new_svn_branch': '',
104 'new_svn_branch': '',
105 'new_svn_tag': '',
105 'new_svn_tag': '',
106 })
106 })
107 return defaults
107 return defaults
108
108
109 @LoginRequired()
109 @LoginRequired()
110 @HasPermissionAllDecorator('hg.admin')
110 @HasPermissionAllDecorator('hg.admin')
111 def settings_vcs(self):
111 def settings_vcs(self):
112 c = self.load_default_context()
112 c = self.load_default_context()
113 c.active = 'vcs'
113 c.active = 'vcs'
114 model = VcsSettingsModel()
114 model = VcsSettingsModel()
115 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
115 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
116 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
116 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
117
117
118 settings = self.request.registry.settings
118 settings = self.request.registry.settings
119 c.svn_proxy_generate_config = settings[generate_config]
119 c.svn_proxy_generate_config = settings[generate_config]
120
120
121 defaults = self._form_defaults()
121 defaults = self._form_defaults()
122
122
123 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
123 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
124
124
125 data = render('rhodecode:templates/admin/settings/settings.mako',
125 data = render('rhodecode:templates/admin/settings/settings.mako',
126 self._get_template_context(c), self.request)
126 self._get_template_context(c), self.request)
127 html = formencode.htmlfill.render(
127 html = formencode.htmlfill.render(
128 data,
128 data,
129 defaults=defaults,
129 defaults=defaults,
130 encoding="UTF-8",
130 encoding="UTF-8",
131 force_defaults=False
131 force_defaults=False
132 )
132 )
133 return Response(html)
133 return Response(html)
134
134
135 @LoginRequired()
135 @LoginRequired()
136 @HasPermissionAllDecorator('hg.admin')
136 @HasPermissionAllDecorator('hg.admin')
137 @CSRFRequired()
137 @CSRFRequired()
138 def settings_vcs_update(self):
138 def settings_vcs_update(self):
139 _ = self.request.translate
139 _ = self.request.translate
140 c = self.load_default_context()
140 c = self.load_default_context()
141 c.active = 'vcs'
141 c.active = 'vcs'
142
142
143 model = VcsSettingsModel()
143 model = VcsSettingsModel()
144 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
144 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
145 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
145 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
146
146
147 settings = self.request.registry.settings
147 settings = self.request.registry.settings
148 c.svn_proxy_generate_config = settings[generate_config]
148 c.svn_proxy_generate_config = settings[generate_config]
149
149
150 application_form = ApplicationUiSettingsForm(self.request.translate)()
150 application_form = ApplicationUiSettingsForm(self.request.translate)()
151
151
152 try:
152 try:
153 form_result = application_form.to_python(dict(self.request.POST))
153 form_result = application_form.to_python(dict(self.request.POST))
154 except formencode.Invalid as errors:
154 except formencode.Invalid as errors:
155 h.flash(
155 h.flash(
156 _("Some form inputs contain invalid data."),
156 _("Some form inputs contain invalid data."),
157 category='error')
157 category='error')
158 data = render('rhodecode:templates/admin/settings/settings.mako',
158 data = render('rhodecode:templates/admin/settings/settings.mako',
159 self._get_template_context(c), self.request)
159 self._get_template_context(c), self.request)
160 html = formencode.htmlfill.render(
160 html = formencode.htmlfill.render(
161 data,
161 data,
162 defaults=errors.value,
162 defaults=errors.value,
163 errors=errors.error_dict or {},
163 errors=errors.unpack_errors() or {},
164 prefix_error=False,
164 prefix_error=False,
165 encoding="UTF-8",
165 encoding="UTF-8",
166 force_defaults=False
166 force_defaults=False
167 )
167 )
168 return Response(html)
168 return Response(html)
169
169
170 try:
170 try:
171 if c.visual.allow_repo_location_change:
171 if c.visual.allow_repo_location_change:
172 model.update_global_path_setting(form_result['paths_root_path'])
172 model.update_global_path_setting(form_result['paths_root_path'])
173
173
174 model.update_global_ssl_setting(form_result['web_push_ssl'])
174 model.update_global_ssl_setting(form_result['web_push_ssl'])
175 model.update_global_hook_settings(form_result)
175 model.update_global_hook_settings(form_result)
176
176
177 model.create_or_update_global_svn_settings(form_result)
177 model.create_or_update_global_svn_settings(form_result)
178 model.create_or_update_global_hg_settings(form_result)
178 model.create_or_update_global_hg_settings(form_result)
179 model.create_or_update_global_git_settings(form_result)
179 model.create_or_update_global_git_settings(form_result)
180 model.create_or_update_global_pr_settings(form_result)
180 model.create_or_update_global_pr_settings(form_result)
181 except Exception:
181 except Exception:
182 log.exception("Exception while updating settings")
182 log.exception("Exception while updating settings")
183 h.flash(_('Error occurred during updating '
183 h.flash(_('Error occurred during updating '
184 'application settings'), category='error')
184 'application settings'), category='error')
185 else:
185 else:
186 Session().commit()
186 Session().commit()
187 h.flash(_('Updated VCS settings'), category='success')
187 h.flash(_('Updated VCS settings'), category='success')
188 raise HTTPFound(h.route_path('admin_settings_vcs'))
188 raise HTTPFound(h.route_path('admin_settings_vcs'))
189
189
190 data = render('rhodecode:templates/admin/settings/settings.mako',
190 data = render('rhodecode:templates/admin/settings/settings.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=self._form_defaults(),
194 defaults=self._form_defaults(),
195 encoding="UTF-8",
195 encoding="UTF-8",
196 force_defaults=False
196 force_defaults=False
197 )
197 )
198 return Response(html)
198 return Response(html)
199
199
200 @LoginRequired()
200 @LoginRequired()
201 @HasPermissionAllDecorator('hg.admin')
201 @HasPermissionAllDecorator('hg.admin')
202 @CSRFRequired()
202 @CSRFRequired()
203 def settings_vcs_delete_svn_pattern(self):
203 def settings_vcs_delete_svn_pattern(self):
204 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
204 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
205 model = VcsSettingsModel()
205 model = VcsSettingsModel()
206 try:
206 try:
207 model.delete_global_svn_pattern(delete_pattern_id)
207 model.delete_global_svn_pattern(delete_pattern_id)
208 except SettingNotFound:
208 except SettingNotFound:
209 log.exception(
209 log.exception(
210 'Failed to delete svn_pattern with id %s', delete_pattern_id)
210 'Failed to delete svn_pattern with id %s', delete_pattern_id)
211 raise HTTPNotFound()
211 raise HTTPNotFound()
212
212
213 Session().commit()
213 Session().commit()
214 return True
214 return True
215
215
216 @LoginRequired()
216 @LoginRequired()
217 @HasPermissionAllDecorator('hg.admin')
217 @HasPermissionAllDecorator('hg.admin')
218 def settings_mapping(self):
218 def settings_mapping(self):
219 c = self.load_default_context()
219 c = self.load_default_context()
220 c.active = 'mapping'
220 c.active = 'mapping'
221
221
222 data = render('rhodecode:templates/admin/settings/settings.mako',
222 data = render('rhodecode:templates/admin/settings/settings.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=self._form_defaults(),
226 defaults=self._form_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 @CSRFRequired()
234 @CSRFRequired()
235 def settings_mapping_update(self):
235 def settings_mapping_update(self):
236 _ = self.request.translate
236 _ = self.request.translate
237 c = self.load_default_context()
237 c = self.load_default_context()
238 c.active = 'mapping'
238 c.active = 'mapping'
239 rm_obsolete = self.request.POST.get('destroy', False)
239 rm_obsolete = self.request.POST.get('destroy', False)
240 invalidate_cache = self.request.POST.get('invalidate', False)
240 invalidate_cache = self.request.POST.get('invalidate', False)
241 log.debug('rescanning repo location with destroy obsolete=%s', rm_obsolete)
241 log.debug('rescanning repo location with destroy obsolete=%s', rm_obsolete)
242
242
243 if invalidate_cache:
243 if invalidate_cache:
244 log.debug('invalidating all repositories cache')
244 log.debug('invalidating all repositories cache')
245 for repo in Repository.get_all():
245 for repo in Repository.get_all():
246 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
246 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
247
247
248 filesystem_repos = ScmModel().repo_scan()
248 filesystem_repos = ScmModel().repo_scan()
249 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
249 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
250 PermissionModel().trigger_permission_flush()
250 PermissionModel().trigger_permission_flush()
251
251
252 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
252 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
253 h.flash(_('Repositories successfully '
253 h.flash(_('Repositories successfully '
254 'rescanned added: %s ; removed: %s') %
254 'rescanned added: %s ; removed: %s') %
255 (_repr(added), _repr(removed)),
255 (_repr(added), _repr(removed)),
256 category='success')
256 category='success')
257 raise HTTPFound(h.route_path('admin_settings_mapping'))
257 raise HTTPFound(h.route_path('admin_settings_mapping'))
258
258
259 @LoginRequired()
259 @LoginRequired()
260 @HasPermissionAllDecorator('hg.admin')
260 @HasPermissionAllDecorator('hg.admin')
261 def settings_global(self):
261 def settings_global(self):
262 c = self.load_default_context()
262 c = self.load_default_context()
263 c.active = 'global'
263 c.active = 'global'
264 c.personal_repo_group_default_pattern = RepoGroupModel()\
264 c.personal_repo_group_default_pattern = RepoGroupModel()\
265 .get_personal_group_name_pattern()
265 .get_personal_group_name_pattern()
266
266
267 data = render('rhodecode:templates/admin/settings/settings.mako',
267 data = render('rhodecode:templates/admin/settings/settings.mako',
268 self._get_template_context(c), self.request)
268 self._get_template_context(c), self.request)
269 html = formencode.htmlfill.render(
269 html = formencode.htmlfill.render(
270 data,
270 data,
271 defaults=self._form_defaults(),
271 defaults=self._form_defaults(),
272 encoding="UTF-8",
272 encoding="UTF-8",
273 force_defaults=False
273 force_defaults=False
274 )
274 )
275 return Response(html)
275 return Response(html)
276
276
277 @LoginRequired()
277 @LoginRequired()
278 @HasPermissionAllDecorator('hg.admin')
278 @HasPermissionAllDecorator('hg.admin')
279 @CSRFRequired()
279 @CSRFRequired()
280 def settings_global_update(self):
280 def settings_global_update(self):
281 _ = self.request.translate
281 _ = self.request.translate
282 c = self.load_default_context()
282 c = self.load_default_context()
283 c.active = 'global'
283 c.active = 'global'
284 c.personal_repo_group_default_pattern = RepoGroupModel()\
284 c.personal_repo_group_default_pattern = RepoGroupModel()\
285 .get_personal_group_name_pattern()
285 .get_personal_group_name_pattern()
286 application_form = ApplicationSettingsForm(self.request.translate)()
286 application_form = ApplicationSettingsForm(self.request.translate)()
287 try:
287 try:
288 form_result = application_form.to_python(dict(self.request.POST))
288 form_result = application_form.to_python(dict(self.request.POST))
289 except formencode.Invalid as errors:
289 except formencode.Invalid as errors:
290 h.flash(
290 h.flash(
291 _("Some form inputs contain invalid data."),
291 _("Some form inputs contain invalid data."),
292 category='error')
292 category='error')
293 data = render('rhodecode:templates/admin/settings/settings.mako',
293 data = render('rhodecode:templates/admin/settings/settings.mako',
294 self._get_template_context(c), self.request)
294 self._get_template_context(c), self.request)
295 html = formencode.htmlfill.render(
295 html = formencode.htmlfill.render(
296 data,
296 data,
297 defaults=errors.value,
297 defaults=errors.value,
298 errors=errors.error_dict or {},
298 errors=errors.unpack_errors() or {},
299 prefix_error=False,
299 prefix_error=False,
300 encoding="UTF-8",
300 encoding="UTF-8",
301 force_defaults=False
301 force_defaults=False
302 )
302 )
303 return Response(html)
303 return Response(html)
304
304
305 settings = [
305 settings = [
306 ('title', 'rhodecode_title', 'unicode'),
306 ('title', 'rhodecode_title', 'unicode'),
307 ('realm', 'rhodecode_realm', 'unicode'),
307 ('realm', 'rhodecode_realm', 'unicode'),
308 ('pre_code', 'rhodecode_pre_code', 'unicode'),
308 ('pre_code', 'rhodecode_pre_code', 'unicode'),
309 ('post_code', 'rhodecode_post_code', 'unicode'),
309 ('post_code', 'rhodecode_post_code', 'unicode'),
310 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
310 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
311 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
311 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
312 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
312 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
313 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
313 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
314 ]
314 ]
315 try:
315 try:
316 for setting, form_key, type_ in settings:
316 for setting, form_key, type_ in settings:
317 sett = SettingsModel().create_or_update_setting(
317 sett = SettingsModel().create_or_update_setting(
318 setting, form_result[form_key], type_)
318 setting, form_result[form_key], type_)
319 Session().add(sett)
319 Session().add(sett)
320
320
321 Session().commit()
321 Session().commit()
322 SettingsModel().invalidate_settings_cache()
322 SettingsModel().invalidate_settings_cache()
323 h.flash(_('Updated application settings'), category='success')
323 h.flash(_('Updated application settings'), category='success')
324 except Exception:
324 except Exception:
325 log.exception("Exception while updating application settings")
325 log.exception("Exception while updating application settings")
326 h.flash(
326 h.flash(
327 _('Error occurred during updating application settings'),
327 _('Error occurred during updating application settings'),
328 category='error')
328 category='error')
329
329
330 raise HTTPFound(h.route_path('admin_settings_global'))
330 raise HTTPFound(h.route_path('admin_settings_global'))
331
331
332 @LoginRequired()
332 @LoginRequired()
333 @HasPermissionAllDecorator('hg.admin')
333 @HasPermissionAllDecorator('hg.admin')
334 def settings_visual(self):
334 def settings_visual(self):
335 c = self.load_default_context()
335 c = self.load_default_context()
336 c.active = 'visual'
336 c.active = 'visual'
337
337
338 data = render('rhodecode:templates/admin/settings/settings.mako',
338 data = render('rhodecode:templates/admin/settings/settings.mako',
339 self._get_template_context(c), self.request)
339 self._get_template_context(c), self.request)
340 html = formencode.htmlfill.render(
340 html = formencode.htmlfill.render(
341 data,
341 data,
342 defaults=self._form_defaults(),
342 defaults=self._form_defaults(),
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
347
348 @LoginRequired()
348 @LoginRequired()
349 @HasPermissionAllDecorator('hg.admin')
349 @HasPermissionAllDecorator('hg.admin')
350 @CSRFRequired()
350 @CSRFRequired()
351 def settings_visual_update(self):
351 def settings_visual_update(self):
352 _ = self.request.translate
352 _ = self.request.translate
353 c = self.load_default_context()
353 c = self.load_default_context()
354 c.active = 'visual'
354 c.active = 'visual'
355 application_form = ApplicationVisualisationForm(self.request.translate)()
355 application_form = ApplicationVisualisationForm(self.request.translate)()
356 try:
356 try:
357 form_result = application_form.to_python(dict(self.request.POST))
357 form_result = application_form.to_python(dict(self.request.POST))
358 except formencode.Invalid as errors:
358 except formencode.Invalid as errors:
359 h.flash(
359 h.flash(
360 _("Some form inputs contain invalid data."),
360 _("Some form inputs contain invalid data."),
361 category='error')
361 category='error')
362 data = render('rhodecode:templates/admin/settings/settings.mako',
362 data = render('rhodecode:templates/admin/settings/settings.mako',
363 self._get_template_context(c), self.request)
363 self._get_template_context(c), self.request)
364 html = formencode.htmlfill.render(
364 html = formencode.htmlfill.render(
365 data,
365 data,
366 defaults=errors.value,
366 defaults=errors.value,
367 errors=errors.error_dict or {},
367 errors=errors.unpack_errors() or {},
368 prefix_error=False,
368 prefix_error=False,
369 encoding="UTF-8",
369 encoding="UTF-8",
370 force_defaults=False
370 force_defaults=False
371 )
371 )
372 return Response(html)
372 return Response(html)
373
373
374 try:
374 try:
375 settings = [
375 settings = [
376 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
376 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
377 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
377 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
378 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
378 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
379 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
379 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
380 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
380 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
381 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
381 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
382 ('show_version', 'rhodecode_show_version', 'bool'),
382 ('show_version', 'rhodecode_show_version', 'bool'),
383 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
383 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
384 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
384 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
385 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
385 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
386 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
386 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
387 ('clone_uri_id_tmpl', 'rhodecode_clone_uri_id_tmpl', 'unicode'),
387 ('clone_uri_id_tmpl', 'rhodecode_clone_uri_id_tmpl', 'unicode'),
388 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
388 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
389 ('support_url', 'rhodecode_support_url', 'unicode'),
389 ('support_url', 'rhodecode_support_url', 'unicode'),
390 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
390 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
391 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
391 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
392 ]
392 ]
393 for setting, form_key, type_ in settings:
393 for setting, form_key, type_ in settings:
394 sett = SettingsModel().create_or_update_setting(
394 sett = SettingsModel().create_or_update_setting(
395 setting, form_result[form_key], type_)
395 setting, form_result[form_key], type_)
396 Session().add(sett)
396 Session().add(sett)
397
397
398 Session().commit()
398 Session().commit()
399 SettingsModel().invalidate_settings_cache()
399 SettingsModel().invalidate_settings_cache()
400 h.flash(_('Updated visualisation settings'), category='success')
400 h.flash(_('Updated visualisation settings'), category='success')
401 except Exception:
401 except Exception:
402 log.exception("Exception updating visualization settings")
402 log.exception("Exception updating visualization settings")
403 h.flash(_('Error occurred during updating '
403 h.flash(_('Error occurred during updating '
404 'visualisation settings'),
404 'visualisation settings'),
405 category='error')
405 category='error')
406
406
407 raise HTTPFound(h.route_path('admin_settings_visual'))
407 raise HTTPFound(h.route_path('admin_settings_visual'))
408
408
409 @LoginRequired()
409 @LoginRequired()
410 @HasPermissionAllDecorator('hg.admin')
410 @HasPermissionAllDecorator('hg.admin')
411 def settings_issuetracker(self):
411 def settings_issuetracker(self):
412 c = self.load_default_context()
412 c = self.load_default_context()
413 c.active = 'issuetracker'
413 c.active = 'issuetracker'
414 defaults = c.rc_config
414 defaults = c.rc_config
415
415
416 entry_key = 'rhodecode_issuetracker_pat_'
416 entry_key = 'rhodecode_issuetracker_pat_'
417
417
418 c.issuetracker_entries = {}
418 c.issuetracker_entries = {}
419 for k, v in defaults.items():
419 for k, v in defaults.items():
420 if k.startswith(entry_key):
420 if k.startswith(entry_key):
421 uid = k[len(entry_key):]
421 uid = k[len(entry_key):]
422 c.issuetracker_entries[uid] = None
422 c.issuetracker_entries[uid] = None
423
423
424 for uid in c.issuetracker_entries:
424 for uid in c.issuetracker_entries:
425 c.issuetracker_entries[uid] = AttributeDict({
425 c.issuetracker_entries[uid] = AttributeDict({
426 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
426 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
427 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
427 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
428 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
428 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
429 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
429 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
430 })
430 })
431
431
432 return self._get_template_context(c)
432 return self._get_template_context(c)
433
433
434 @LoginRequired()
434 @LoginRequired()
435 @HasPermissionAllDecorator('hg.admin')
435 @HasPermissionAllDecorator('hg.admin')
436 @CSRFRequired()
436 @CSRFRequired()
437 def settings_issuetracker_test(self):
437 def settings_issuetracker_test(self):
438 error_container = []
438 error_container = []
439
439
440 urlified_commit = h.urlify_commit_message(
440 urlified_commit = h.urlify_commit_message(
441 self.request.POST.get('test_text', ''),
441 self.request.POST.get('test_text', ''),
442 'repo_group/test_repo1', error_container=error_container)
442 'repo_group/test_repo1', error_container=error_container)
443 if error_container:
443 if error_container:
444 def converter(inp):
444 def converter(inp):
445 return h.html_escape(inp)
445 return h.html_escape(inp)
446
446
447 return 'ERRORS: ' + '\n'.join(map(converter, error_container))
447 return 'ERRORS: ' + '\n'.join(map(converter, error_container))
448
448
449 return urlified_commit
449 return urlified_commit
450
450
451 @LoginRequired()
451 @LoginRequired()
452 @HasPermissionAllDecorator('hg.admin')
452 @HasPermissionAllDecorator('hg.admin')
453 @CSRFRequired()
453 @CSRFRequired()
454 def settings_issuetracker_update(self):
454 def settings_issuetracker_update(self):
455 _ = self.request.translate
455 _ = self.request.translate
456 self.load_default_context()
456 self.load_default_context()
457 settings_model = IssueTrackerSettingsModel()
457 settings_model = IssueTrackerSettingsModel()
458
458
459 try:
459 try:
460 form = IssueTrackerPatternsForm(self.request.translate)()
460 form = IssueTrackerPatternsForm(self.request.translate)()
461 data = form.to_python(self.request.POST)
461 data = form.to_python(self.request.POST)
462 except formencode.Invalid as errors:
462 except formencode.Invalid as errors:
463 log.exception('Failed to add new pattern')
463 log.exception('Failed to add new pattern')
464 error = errors
464 error = errors
465 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
465 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
466 category='error')
466 category='error')
467 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
467 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
468
468
469 if data:
469 if data:
470 for uid in data.get('delete_patterns', []):
470 for uid in data.get('delete_patterns', []):
471 settings_model.delete_entries(uid)
471 settings_model.delete_entries(uid)
472
472
473 for pattern in data.get('patterns', []):
473 for pattern in data.get('patterns', []):
474 for setting, value, type_ in pattern:
474 for setting, value, type_ in pattern:
475 sett = settings_model.create_or_update_setting(
475 sett = settings_model.create_or_update_setting(
476 setting, value, type_)
476 setting, value, type_)
477 Session().add(sett)
477 Session().add(sett)
478
478
479 Session().commit()
479 Session().commit()
480
480
481 SettingsModel().invalidate_settings_cache()
481 SettingsModel().invalidate_settings_cache()
482 h.flash(_('Updated issue tracker entries'), category='success')
482 h.flash(_('Updated issue tracker entries'), category='success')
483 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
483 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
484
484
485 @LoginRequired()
485 @LoginRequired()
486 @HasPermissionAllDecorator('hg.admin')
486 @HasPermissionAllDecorator('hg.admin')
487 @CSRFRequired()
487 @CSRFRequired()
488 def settings_issuetracker_delete(self):
488 def settings_issuetracker_delete(self):
489 _ = self.request.translate
489 _ = self.request.translate
490 self.load_default_context()
490 self.load_default_context()
491 uid = self.request.POST.get('uid')
491 uid = self.request.POST.get('uid')
492 try:
492 try:
493 IssueTrackerSettingsModel().delete_entries(uid)
493 IssueTrackerSettingsModel().delete_entries(uid)
494 except Exception:
494 except Exception:
495 log.exception('Failed to delete issue tracker setting %s', uid)
495 log.exception('Failed to delete issue tracker setting %s', uid)
496 raise HTTPNotFound()
496 raise HTTPNotFound()
497
497
498 SettingsModel().invalidate_settings_cache()
498 SettingsModel().invalidate_settings_cache()
499 h.flash(_('Removed issue tracker entry.'), category='success')
499 h.flash(_('Removed issue tracker entry.'), category='success')
500
500
501 return {'deleted': uid}
501 return {'deleted': uid}
502
502
503 @LoginRequired()
503 @LoginRequired()
504 @HasPermissionAllDecorator('hg.admin')
504 @HasPermissionAllDecorator('hg.admin')
505 def settings_email(self):
505 def settings_email(self):
506 c = self.load_default_context()
506 c = self.load_default_context()
507 c.active = 'email'
507 c.active = 'email'
508 c.rhodecode_ini = rhodecode.CONFIG
508 c.rhodecode_ini = rhodecode.CONFIG
509
509
510 data = render('rhodecode:templates/admin/settings/settings.mako',
510 data = render('rhodecode:templates/admin/settings/settings.mako',
511 self._get_template_context(c), self.request)
511 self._get_template_context(c), self.request)
512 html = formencode.htmlfill.render(
512 html = formencode.htmlfill.render(
513 data,
513 data,
514 defaults=self._form_defaults(),
514 defaults=self._form_defaults(),
515 encoding="UTF-8",
515 encoding="UTF-8",
516 force_defaults=False
516 force_defaults=False
517 )
517 )
518 return Response(html)
518 return Response(html)
519
519
520 @LoginRequired()
520 @LoginRequired()
521 @HasPermissionAllDecorator('hg.admin')
521 @HasPermissionAllDecorator('hg.admin')
522 @CSRFRequired()
522 @CSRFRequired()
523 def settings_email_update(self):
523 def settings_email_update(self):
524 _ = self.request.translate
524 _ = self.request.translate
525 c = self.load_default_context()
525 c = self.load_default_context()
526 c.active = 'email'
526 c.active = 'email'
527
527
528 test_email = self.request.POST.get('test_email')
528 test_email = self.request.POST.get('test_email')
529
529
530 if not test_email:
530 if not test_email:
531 h.flash(_('Please enter email address'), category='error')
531 h.flash(_('Please enter email address'), category='error')
532 raise HTTPFound(h.route_path('admin_settings_email'))
532 raise HTTPFound(h.route_path('admin_settings_email'))
533
533
534 email_kwargs = {
534 email_kwargs = {
535 'date': datetime.datetime.now(),
535 'date': datetime.datetime.now(),
536 'user': self._rhodecode_db_user
536 'user': self._rhodecode_db_user
537 }
537 }
538
538
539 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
539 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
540 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
540 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
541
541
542 recipients = [test_email] if test_email else None
542 recipients = [test_email] if test_email else None
543
543
544 run_task(tasks.send_email, recipients, subject,
544 run_task(tasks.send_email, recipients, subject,
545 email_body_plaintext, email_body)
545 email_body_plaintext, email_body)
546
546
547 h.flash(_('Send email task created'), category='success')
547 h.flash(_('Send email task created'), category='success')
548 raise HTTPFound(h.route_path('admin_settings_email'))
548 raise HTTPFound(h.route_path('admin_settings_email'))
549
549
550 @LoginRequired()
550 @LoginRequired()
551 @HasPermissionAllDecorator('hg.admin')
551 @HasPermissionAllDecorator('hg.admin')
552 def settings_hooks(self):
552 def settings_hooks(self):
553 c = self.load_default_context()
553 c = self.load_default_context()
554 c.active = 'hooks'
554 c.active = 'hooks'
555
555
556 model = SettingsModel()
556 model = SettingsModel()
557 c.hooks = model.get_builtin_hooks()
557 c.hooks = model.get_builtin_hooks()
558 c.custom_hooks = model.get_custom_hooks()
558 c.custom_hooks = model.get_custom_hooks()
559
559
560 data = render('rhodecode:templates/admin/settings/settings.mako',
560 data = render('rhodecode:templates/admin/settings/settings.mako',
561 self._get_template_context(c), self.request)
561 self._get_template_context(c), self.request)
562 html = formencode.htmlfill.render(
562 html = formencode.htmlfill.render(
563 data,
563 data,
564 defaults=self._form_defaults(),
564 defaults=self._form_defaults(),
565 encoding="UTF-8",
565 encoding="UTF-8",
566 force_defaults=False
566 force_defaults=False
567 )
567 )
568 return Response(html)
568 return Response(html)
569
569
570 @LoginRequired()
570 @LoginRequired()
571 @HasPermissionAllDecorator('hg.admin')
571 @HasPermissionAllDecorator('hg.admin')
572 @CSRFRequired()
572 @CSRFRequired()
573 def settings_hooks_update(self):
573 def settings_hooks_update(self):
574 _ = self.request.translate
574 _ = self.request.translate
575 c = self.load_default_context()
575 c = self.load_default_context()
576 c.active = 'hooks'
576 c.active = 'hooks'
577 if c.visual.allow_custom_hooks_settings:
577 if c.visual.allow_custom_hooks_settings:
578 ui_key = self.request.POST.get('new_hook_ui_key')
578 ui_key = self.request.POST.get('new_hook_ui_key')
579 ui_value = self.request.POST.get('new_hook_ui_value')
579 ui_value = self.request.POST.get('new_hook_ui_value')
580
580
581 hook_id = self.request.POST.get('hook_id')
581 hook_id = self.request.POST.get('hook_id')
582 new_hook = False
582 new_hook = False
583
583
584 model = SettingsModel()
584 model = SettingsModel()
585 try:
585 try:
586 if ui_value and ui_key:
586 if ui_value and ui_key:
587 model.create_or_update_hook(ui_key, ui_value)
587 model.create_or_update_hook(ui_key, ui_value)
588 h.flash(_('Added new hook'), category='success')
588 h.flash(_('Added new hook'), category='success')
589 new_hook = True
589 new_hook = True
590 elif hook_id:
590 elif hook_id:
591 RhodeCodeUi.delete(hook_id)
591 RhodeCodeUi.delete(hook_id)
592 Session().commit()
592 Session().commit()
593
593
594 # check for edits
594 # check for edits
595 update = False
595 update = False
596 _d = self.request.POST.dict_of_lists()
596 _d = self.request.POST.dict_of_lists()
597 for k, v in zip(_d.get('hook_ui_key', []),
597 for k, v in zip(_d.get('hook_ui_key', []),
598 _d.get('hook_ui_value_new', [])):
598 _d.get('hook_ui_value_new', [])):
599 model.create_or_update_hook(k, v)
599 model.create_or_update_hook(k, v)
600 update = True
600 update = True
601
601
602 if update and not new_hook:
602 if update and not new_hook:
603 h.flash(_('Updated hooks'), category='success')
603 h.flash(_('Updated hooks'), category='success')
604 Session().commit()
604 Session().commit()
605 except Exception:
605 except Exception:
606 log.exception("Exception during hook creation")
606 log.exception("Exception during hook creation")
607 h.flash(_('Error occurred during hook creation'),
607 h.flash(_('Error occurred during hook creation'),
608 category='error')
608 category='error')
609
609
610 raise HTTPFound(h.route_path('admin_settings_hooks'))
610 raise HTTPFound(h.route_path('admin_settings_hooks'))
611
611
612 @LoginRequired()
612 @LoginRequired()
613 @HasPermissionAllDecorator('hg.admin')
613 @HasPermissionAllDecorator('hg.admin')
614 def settings_search(self):
614 def settings_search(self):
615 c = self.load_default_context()
615 c = self.load_default_context()
616 c.active = 'search'
616 c.active = 'search'
617
617
618 c.searcher = searcher_from_config(self.request.registry.settings)
618 c.searcher = searcher_from_config(self.request.registry.settings)
619 c.statistics = c.searcher.statistics(self.request.translate)
619 c.statistics = c.searcher.statistics(self.request.translate)
620
620
621 return self._get_template_context(c)
621 return self._get_template_context(c)
622
622
623 @LoginRequired()
623 @LoginRequired()
624 @HasPermissionAllDecorator('hg.admin')
624 @HasPermissionAllDecorator('hg.admin')
625 def settings_automation(self):
625 def settings_automation(self):
626 c = self.load_default_context()
626 c = self.load_default_context()
627 c.active = 'automation'
627 c.active = 'automation'
628
628
629 return self._get_template_context(c)
629 return self._get_template_context(c)
630
630
631 @LoginRequired()
631 @LoginRequired()
632 @HasPermissionAllDecorator('hg.admin')
632 @HasPermissionAllDecorator('hg.admin')
633 def settings_labs(self):
633 def settings_labs(self):
634 c = self.load_default_context()
634 c = self.load_default_context()
635 if not c.labs_active:
635 if not c.labs_active:
636 raise HTTPFound(h.route_path('admin_settings'))
636 raise HTTPFound(h.route_path('admin_settings'))
637
637
638 c.active = 'labs'
638 c.active = 'labs'
639 c.lab_settings = _LAB_SETTINGS
639 c.lab_settings = _LAB_SETTINGS
640
640
641 data = render('rhodecode:templates/admin/settings/settings.mako',
641 data = render('rhodecode:templates/admin/settings/settings.mako',
642 self._get_template_context(c), self.request)
642 self._get_template_context(c), self.request)
643 html = formencode.htmlfill.render(
643 html = formencode.htmlfill.render(
644 data,
644 data,
645 defaults=self._form_defaults(),
645 defaults=self._form_defaults(),
646 encoding="UTF-8",
646 encoding="UTF-8",
647 force_defaults=False
647 force_defaults=False
648 )
648 )
649 return Response(html)
649 return Response(html)
650
650
651 @LoginRequired()
651 @LoginRequired()
652 @HasPermissionAllDecorator('hg.admin')
652 @HasPermissionAllDecorator('hg.admin')
653 @CSRFRequired()
653 @CSRFRequired()
654 def settings_labs_update(self):
654 def settings_labs_update(self):
655 _ = self.request.translate
655 _ = self.request.translate
656 c = self.load_default_context()
656 c = self.load_default_context()
657 c.active = 'labs'
657 c.active = 'labs'
658
658
659 application_form = LabsSettingsForm(self.request.translate)()
659 application_form = LabsSettingsForm(self.request.translate)()
660 try:
660 try:
661 form_result = application_form.to_python(dict(self.request.POST))
661 form_result = application_form.to_python(dict(self.request.POST))
662 except formencode.Invalid as errors:
662 except formencode.Invalid as errors:
663 h.flash(
663 h.flash(
664 _("Some form inputs contain invalid data."),
664 _("Some form inputs contain invalid data."),
665 category='error')
665 category='error')
666 data = render('rhodecode:templates/admin/settings/settings.mako',
666 data = render('rhodecode:templates/admin/settings/settings.mako',
667 self._get_template_context(c), self.request)
667 self._get_template_context(c), self.request)
668 html = formencode.htmlfill.render(
668 html = formencode.htmlfill.render(
669 data,
669 data,
670 defaults=errors.value,
670 defaults=errors.value,
671 errors=errors.error_dict or {},
671 errors=errors.unpack_errors() or {},
672 prefix_error=False,
672 prefix_error=False,
673 encoding="UTF-8",
673 encoding="UTF-8",
674 force_defaults=False
674 force_defaults=False
675 )
675 )
676 return Response(html)
676 return Response(html)
677
677
678 try:
678 try:
679 session = Session()
679 session = Session()
680 for setting in _LAB_SETTINGS:
680 for setting in _LAB_SETTINGS:
681 setting_name = setting.key[len('rhodecode_'):]
681 setting_name = setting.key[len('rhodecode_'):]
682 sett = SettingsModel().create_or_update_setting(
682 sett = SettingsModel().create_or_update_setting(
683 setting_name, form_result[setting.key], setting.type)
683 setting_name, form_result[setting.key], setting.type)
684 session.add(sett)
684 session.add(sett)
685
685
686 except Exception:
686 except Exception:
687 log.exception('Exception while updating lab settings')
687 log.exception('Exception while updating lab settings')
688 h.flash(_('Error occurred during updating labs settings'),
688 h.flash(_('Error occurred during updating labs settings'),
689 category='error')
689 category='error')
690 else:
690 else:
691 Session().commit()
691 Session().commit()
692 SettingsModel().invalidate_settings_cache()
692 SettingsModel().invalidate_settings_cache()
693 h.flash(_('Updated Labs settings'), category='success')
693 h.flash(_('Updated Labs settings'), category='success')
694 raise HTTPFound(h.route_path('admin_settings_labs'))
694 raise HTTPFound(h.route_path('admin_settings_labs'))
695
695
696 data = render('rhodecode:templates/admin/settings/settings.mako',
696 data = render('rhodecode:templates/admin/settings/settings.mako',
697 self._get_template_context(c), self.request)
697 self._get_template_context(c), self.request)
698 html = formencode.htmlfill.render(
698 html = formencode.htmlfill.render(
699 data,
699 data,
700 defaults=self._form_defaults(),
700 defaults=self._form_defaults(),
701 encoding="UTF-8",
701 encoding="UTF-8",
702 force_defaults=False
702 force_defaults=False
703 )
703 )
704 return Response(html)
704 return Response(html)
705
705
706
706
707 # :param key: name of the setting including the 'rhodecode_' prefix
707 # :param key: name of the setting including the 'rhodecode_' prefix
708 # :param type: the RhodeCodeSetting type to use.
708 # :param type: the RhodeCodeSetting type to use.
709 # :param group: the i18ned group in which we should dispaly this setting
709 # :param group: the i18ned group in which we should dispaly this setting
710 # :param label: the i18ned label we should display for this setting
710 # :param label: the i18ned label we should display for this setting
711 # :param help: the i18ned help we should dispaly for this setting
711 # :param help: the i18ned help we should dispaly for this setting
712 LabSetting = collections.namedtuple(
712 LabSetting = collections.namedtuple(
713 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
713 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
714
714
715
715
716 # This list has to be kept in sync with the form
716 # This list has to be kept in sync with the form
717 # rhodecode.model.forms.LabsSettingsForm.
717 # rhodecode.model.forms.LabsSettingsForm.
718 _LAB_SETTINGS = [
718 _LAB_SETTINGS = [
719
719
720 ]
720 ]
@@ -1,253 +1,253 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27
27
28 from pyramid.response import Response
28 from pyramid.response import Response
29 from pyramid.renderers import render
29 from pyramid.renderers import render
30
30
31 from rhodecode import events
31 from rhodecode import events
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
34 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
35 from rhodecode.lib import helpers as h, audit_logger
35 from rhodecode.lib import helpers as h, audit_logger
36 from rhodecode.lib.utils2 import safe_unicode
36 from rhodecode.lib.utils2 import safe_unicode
37
37
38 from rhodecode.model.forms import UserGroupForm
38 from rhodecode.model.forms import UserGroupForm
39 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.permission import PermissionModel
40 from rhodecode.model.scm import UserGroupList
40 from rhodecode.model.scm import UserGroupList
41 from rhodecode.model.db import (
41 from rhodecode.model.db import (
42 or_, count, User, UserGroup, UserGroupMember, in_filter_generator)
42 or_, count, User, UserGroup, UserGroupMember, in_filter_generator)
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.model.user_group import UserGroupModel
44 from rhodecode.model.user_group import UserGroupModel
45 from rhodecode.model.db import true
45 from rhodecode.model.db import true
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminUserGroupsView(BaseAppView, DataGridAppView):
50 class AdminUserGroupsView(BaseAppView, DataGridAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54 PermissionModel().set_global_permission_choices(
54 PermissionModel().set_global_permission_choices(
55 c, gettext_translator=self.request.translate)
55 c, gettext_translator=self.request.translate)
56 return c
56 return c
57
57
58 # permission check in data loading of
58 # permission check in data loading of
59 # `user_groups_list_data` via UserGroupList
59 # `user_groups_list_data` via UserGroupList
60 @LoginRequired()
60 @LoginRequired()
61 @NotAnonymous()
61 @NotAnonymous()
62 def user_groups_list(self):
62 def user_groups_list(self):
63 c = self.load_default_context()
63 c = self.load_default_context()
64 return self._get_template_context(c)
64 return self._get_template_context(c)
65
65
66 # permission check inside
66 # permission check inside
67 @LoginRequired()
67 @LoginRequired()
68 @NotAnonymous()
68 @NotAnonymous()
69 def user_groups_list_data(self):
69 def user_groups_list_data(self):
70 self.load_default_context()
70 self.load_default_context()
71 column_map = {
71 column_map = {
72 'active': 'users_group_active',
72 'active': 'users_group_active',
73 'description': 'user_group_description',
73 'description': 'user_group_description',
74 'members': 'members_total',
74 'members': 'members_total',
75 'owner': 'user_username',
75 'owner': 'user_username',
76 'sync': 'group_data'
76 'sync': 'group_data'
77 }
77 }
78 draw, start, limit = self._extract_chunk(self.request)
78 draw, start, limit = self._extract_chunk(self.request)
79 search_q, order_by, order_dir = self._extract_ordering(
79 search_q, order_by, order_dir = self._extract_ordering(
80 self.request, column_map=column_map)
80 self.request, column_map=column_map)
81
81
82 _render = self.request.get_partial_renderer(
82 _render = self.request.get_partial_renderer(
83 'rhodecode:templates/data_table/_dt_elements.mako')
83 'rhodecode:templates/data_table/_dt_elements.mako')
84
84
85 def user_group_name(user_group_name):
85 def user_group_name(user_group_name):
86 return _render("user_group_name", user_group_name)
86 return _render("user_group_name", user_group_name)
87
87
88 def user_group_actions(user_group_id, user_group_name):
88 def user_group_actions(user_group_id, user_group_name):
89 return _render("user_group_actions", user_group_id, user_group_name)
89 return _render("user_group_actions", user_group_id, user_group_name)
90
90
91 def user_profile(username):
91 def user_profile(username):
92 return _render('user_profile', username)
92 return _render('user_profile', username)
93
93
94 _perms = ['usergroup.admin']
94 _perms = ['usergroup.admin']
95 allowed_ids = [-1] + self._rhodecode_user.user_group_acl_ids_from_stack(_perms)
95 allowed_ids = [-1] + self._rhodecode_user.user_group_acl_ids_from_stack(_perms)
96
96
97 user_groups_data_total_count = UserGroup.query()\
97 user_groups_data_total_count = UserGroup.query()\
98 .filter(or_(
98 .filter(or_(
99 # generate multiple IN to fix limitation problems
99 # generate multiple IN to fix limitation problems
100 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
100 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
101 ))\
101 ))\
102 .count()
102 .count()
103
103
104 user_groups_data_total_inactive_count = UserGroup.query()\
104 user_groups_data_total_inactive_count = UserGroup.query()\
105 .filter(or_(
105 .filter(or_(
106 # generate multiple IN to fix limitation problems
106 # generate multiple IN to fix limitation problems
107 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
107 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
108 ))\
108 ))\
109 .filter(UserGroup.users_group_active != true()).count()
109 .filter(UserGroup.users_group_active != true()).count()
110
110
111 member_count = count(UserGroupMember.user_id)
111 member_count = count(UserGroupMember.user_id)
112 base_q = Session.query(
112 base_q = Session.query(
113 UserGroup.users_group_name,
113 UserGroup.users_group_name,
114 UserGroup.user_group_description,
114 UserGroup.user_group_description,
115 UserGroup.users_group_active,
115 UserGroup.users_group_active,
116 UserGroup.users_group_id,
116 UserGroup.users_group_id,
117 UserGroup.group_data,
117 UserGroup.group_data,
118 User,
118 User,
119 member_count.label('member_count')
119 member_count.label('member_count')
120 ) \
120 ) \
121 .filter(or_(
121 .filter(or_(
122 # generate multiple IN to fix limitation problems
122 # generate multiple IN to fix limitation problems
123 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
123 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
124 )) \
124 )) \
125 .outerjoin(UserGroupMember, UserGroupMember.users_group_id == UserGroup.users_group_id) \
125 .outerjoin(UserGroupMember, UserGroupMember.users_group_id == UserGroup.users_group_id) \
126 .join(User, User.user_id == UserGroup.user_id) \
126 .join(User, User.user_id == UserGroup.user_id) \
127 .group_by(UserGroup, User)
127 .group_by(UserGroup, User)
128
128
129 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
129 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
130
130
131 if search_q:
131 if search_q:
132 like_expression = u'%{}%'.format(safe_unicode(search_q))
132 like_expression = u'%{}%'.format(safe_unicode(search_q))
133 base_q = base_q.filter(or_(
133 base_q = base_q.filter(or_(
134 UserGroup.users_group_name.ilike(like_expression),
134 UserGroup.users_group_name.ilike(like_expression),
135 ))
135 ))
136 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
136 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
137
137
138 user_groups_data_total_filtered_count = base_q.count()
138 user_groups_data_total_filtered_count = base_q.count()
139 user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
139 user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
140
140
141 sort_defined = False
141 sort_defined = False
142 if order_by == 'members_total':
142 if order_by == 'members_total':
143 sort_col = member_count
143 sort_col = member_count
144 sort_defined = True
144 sort_defined = True
145 elif order_by == 'user_username':
145 elif order_by == 'user_username':
146 sort_col = User.username
146 sort_col = User.username
147 else:
147 else:
148 sort_col = getattr(UserGroup, order_by, None)
148 sort_col = getattr(UserGroup, order_by, None)
149
149
150 if sort_defined or sort_col:
150 if sort_defined or sort_col:
151 if order_dir == 'asc':
151 if order_dir == 'asc':
152 sort_col = sort_col.asc()
152 sort_col = sort_col.asc()
153 else:
153 else:
154 sort_col = sort_col.desc()
154 sort_col = sort_col.desc()
155
155
156 base_q = base_q.order_by(sort_col)
156 base_q = base_q.order_by(sort_col)
157 base_q = base_q.offset(start).limit(limit)
157 base_q = base_q.offset(start).limit(limit)
158
158
159 # authenticated access to user groups
159 # authenticated access to user groups
160 auth_user_group_list = base_q.all()
160 auth_user_group_list = base_q.all()
161
161
162 user_groups_data = []
162 user_groups_data = []
163 for user_gr in auth_user_group_list:
163 for user_gr in auth_user_group_list:
164 row = {
164 row = {
165 "users_group_name": user_group_name(user_gr.users_group_name),
165 "users_group_name": user_group_name(user_gr.users_group_name),
166 "description": h.escape(user_gr.user_group_description),
166 "description": h.escape(user_gr.user_group_description),
167 "members": user_gr.member_count,
167 "members": user_gr.member_count,
168 # NOTE(marcink): because of advanced query we
168 # NOTE(marcink): because of advanced query we
169 # need to load it like that
169 # need to load it like that
170 "sync": UserGroup._load_sync(
170 "sync": UserGroup._load_sync(
171 UserGroup._load_group_data(user_gr.group_data)),
171 UserGroup._load_group_data(user_gr.group_data)),
172 "active": h.bool2icon(user_gr.users_group_active),
172 "active": h.bool2icon(user_gr.users_group_active),
173 "owner": user_profile(user_gr.User.username),
173 "owner": user_profile(user_gr.User.username),
174 "action": user_group_actions(
174 "action": user_group_actions(
175 user_gr.users_group_id, user_gr.users_group_name)
175 user_gr.users_group_id, user_gr.users_group_name)
176 }
176 }
177 user_groups_data.append(row)
177 user_groups_data.append(row)
178
178
179 data = ({
179 data = ({
180 'draw': draw,
180 'draw': draw,
181 'data': user_groups_data,
181 'data': user_groups_data,
182 'recordsTotal': user_groups_data_total_count,
182 'recordsTotal': user_groups_data_total_count,
183 'recordsTotalInactive': user_groups_data_total_inactive_count,
183 'recordsTotalInactive': user_groups_data_total_inactive_count,
184 'recordsFiltered': user_groups_data_total_filtered_count,
184 'recordsFiltered': user_groups_data_total_filtered_count,
185 'recordsFilteredInactive': user_groups_data_total_filtered_inactive_count,
185 'recordsFilteredInactive': user_groups_data_total_filtered_inactive_count,
186 })
186 })
187
187
188 return data
188 return data
189
189
190 @LoginRequired()
190 @LoginRequired()
191 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
191 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
192 def user_groups_new(self):
192 def user_groups_new(self):
193 c = self.load_default_context()
193 c = self.load_default_context()
194 return self._get_template_context(c)
194 return self._get_template_context(c)
195
195
196 @LoginRequired()
196 @LoginRequired()
197 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
197 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
198 @CSRFRequired()
198 @CSRFRequired()
199 def user_groups_create(self):
199 def user_groups_create(self):
200 _ = self.request.translate
200 _ = self.request.translate
201 c = self.load_default_context()
201 c = self.load_default_context()
202 users_group_form = UserGroupForm(self.request.translate)()
202 users_group_form = UserGroupForm(self.request.translate)()
203
203
204 user_group_name = self.request.POST.get('users_group_name')
204 user_group_name = self.request.POST.get('users_group_name')
205 try:
205 try:
206 form_result = users_group_form.to_python(dict(self.request.POST))
206 form_result = users_group_form.to_python(dict(self.request.POST))
207 user_group = UserGroupModel().create(
207 user_group = UserGroupModel().create(
208 name=form_result['users_group_name'],
208 name=form_result['users_group_name'],
209 description=form_result['user_group_description'],
209 description=form_result['user_group_description'],
210 owner=self._rhodecode_user.user_id,
210 owner=self._rhodecode_user.user_id,
211 active=form_result['users_group_active'])
211 active=form_result['users_group_active'])
212 Session().flush()
212 Session().flush()
213 creation_data = user_group.get_api_data()
213 creation_data = user_group.get_api_data()
214 user_group_name = form_result['users_group_name']
214 user_group_name = form_result['users_group_name']
215
215
216 audit_logger.store_web(
216 audit_logger.store_web(
217 'user_group.create', action_data={'data': creation_data},
217 'user_group.create', action_data={'data': creation_data},
218 user=self._rhodecode_user)
218 user=self._rhodecode_user)
219
219
220 user_group_link = h.link_to(
220 user_group_link = h.link_to(
221 h.escape(user_group_name),
221 h.escape(user_group_name),
222 h.route_path(
222 h.route_path(
223 'edit_user_group', user_group_id=user_group.users_group_id))
223 'edit_user_group', user_group_id=user_group.users_group_id))
224 h.flash(h.literal(_('Created user group %(user_group_link)s')
224 h.flash(h.literal(_('Created user group %(user_group_link)s')
225 % {'user_group_link': user_group_link}),
225 % {'user_group_link': user_group_link}),
226 category='success')
226 category='success')
227 Session().commit()
227 Session().commit()
228 user_group_id = user_group.users_group_id
228 user_group_id = user_group.users_group_id
229 except formencode.Invalid as errors:
229 except formencode.Invalid as errors:
230
230
231 data = render(
231 data = render(
232 'rhodecode:templates/admin/user_groups/user_group_add.mako',
232 'rhodecode:templates/admin/user_groups/user_group_add.mako',
233 self._get_template_context(c), self.request)
233 self._get_template_context(c), self.request)
234 html = formencode.htmlfill.render(
234 html = formencode.htmlfill.render(
235 data,
235 data,
236 defaults=errors.value,
236 defaults=errors.value,
237 errors=errors.error_dict or {},
237 errors=errors.unpack_errors() or {},
238 prefix_error=False,
238 prefix_error=False,
239 encoding="UTF-8",
239 encoding="UTF-8",
240 force_defaults=False
240 force_defaults=False
241 )
241 )
242 return Response(html)
242 return Response(html)
243
243
244 except Exception:
244 except Exception:
245 log.exception("Exception creating user group")
245 log.exception("Exception creating user group")
246 h.flash(_('Error occurred during creation of user group %s') \
246 h.flash(_('Error occurred during creation of user group %s') \
247 % user_group_name, category='error')
247 % user_group_name, category='error')
248 raise HTTPFound(h.route_path('user_groups_new'))
248 raise HTTPFound(h.route_path('user_groups_new'))
249
249
250 PermissionModel().trigger_permission_flush()
250 PermissionModel().trigger_permission_flush()
251
251
252 raise HTTPFound(
252 raise HTTPFound(
253 h.route_path('edit_user_group', user_group_id=user_group_id))
253 h.route_path('edit_user_group', user_group_id=user_group_id))
@@ -1,1321 +1,1322 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.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 import events
30 from rhodecode import events
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
33 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
34 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.authentication.plugins import auth_rhodecode
35 from rhodecode.events import trigger
35 from rhodecode.events import trigger
36 from rhodecode.model.db import true, UserNotice
36 from rhodecode.model.db import true, UserNotice
37
37
38 from rhodecode.lib import audit_logger, rc_cache, auth
38 from rhodecode.lib import audit_logger, rc_cache, auth
39 from rhodecode.lib.exceptions import (
39 from rhodecode.lib.exceptions import (
40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
41 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
42 UserOwnsArtifactsException, DefaultUserException)
42 UserOwnsArtifactsException, DefaultUserException)
43 from rhodecode.lib import ext_json
43 from rhodecode.lib import ext_json
44 from rhodecode.lib.auth import (
44 from rhodecode.lib.auth import (
45 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
45 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
46 from rhodecode.lib import helpers as h
46 from rhodecode.lib import helpers as h
47 from rhodecode.lib.helpers import SqlPage
47 from rhodecode.lib.helpers import SqlPage
48 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
48 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
49 from rhodecode.model.auth_token import AuthTokenModel
49 from rhodecode.model.auth_token import AuthTokenModel
50 from rhodecode.model.forms import (
50 from rhodecode.model.forms import (
51 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
51 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
52 UserExtraEmailForm, UserExtraIpForm)
52 UserExtraEmailForm, UserExtraIpForm)
53 from rhodecode.model.permission import PermissionModel
53 from rhodecode.model.permission import PermissionModel
54 from rhodecode.model.repo_group import RepoGroupModel
54 from rhodecode.model.repo_group import RepoGroupModel
55 from rhodecode.model.ssh_key import SshKeyModel
55 from rhodecode.model.ssh_key import SshKeyModel
56 from rhodecode.model.user import UserModel
56 from rhodecode.model.user import UserModel
57 from rhodecode.model.user_group import UserGroupModel
57 from rhodecode.model.user_group import UserGroupModel
58 from rhodecode.model.db import (
58 from rhodecode.model.db import (
59 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
59 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
60 UserApiKeys, UserSshKeys, RepoGroup)
60 UserApiKeys, UserSshKeys, RepoGroup)
61 from rhodecode.model.meta import Session
61 from rhodecode.model.meta import Session
62
62
63 log = logging.getLogger(__name__)
63 log = logging.getLogger(__name__)
64
64
65
65
66 class AdminUsersView(BaseAppView, DataGridAppView):
66 class AdminUsersView(BaseAppView, DataGridAppView):
67
67
68 def load_default_context(self):
68 def load_default_context(self):
69 c = self._get_local_tmpl_context()
69 c = self._get_local_tmpl_context()
70 return c
70 return c
71
71
72 @LoginRequired()
72 @LoginRequired()
73 @HasPermissionAllDecorator('hg.admin')
73 @HasPermissionAllDecorator('hg.admin')
74 def users_list(self):
74 def users_list(self):
75 c = self.load_default_context()
75 c = self.load_default_context()
76 return self._get_template_context(c)
76 return self._get_template_context(c)
77
77
78 @LoginRequired()
78 @LoginRequired()
79 @HasPermissionAllDecorator('hg.admin')
79 @HasPermissionAllDecorator('hg.admin')
80 def users_list_data(self):
80 def users_list_data(self):
81 self.load_default_context()
81 self.load_default_context()
82 column_map = {
82 column_map = {
83 'first_name': 'name',
83 'first_name': 'name',
84 'last_name': 'lastname',
84 'last_name': 'lastname',
85 }
85 }
86 draw, start, limit = self._extract_chunk(self.request)
86 draw, start, limit = self._extract_chunk(self.request)
87 search_q, order_by, order_dir = self._extract_ordering(
87 search_q, order_by, order_dir = self._extract_ordering(
88 self.request, column_map=column_map)
88 self.request, column_map=column_map)
89 _render = self.request.get_partial_renderer(
89 _render = self.request.get_partial_renderer(
90 'rhodecode:templates/data_table/_dt_elements.mako')
90 'rhodecode:templates/data_table/_dt_elements.mako')
91
91
92 def user_actions(user_id, username):
92 def user_actions(user_id, username):
93 return _render("user_actions", user_id, username)
93 return _render("user_actions", user_id, username)
94
94
95 users_data_total_count = User.query()\
95 users_data_total_count = User.query()\
96 .filter(User.username != User.DEFAULT_USER) \
96 .filter(User.username != User.DEFAULT_USER) \
97 .count()
97 .count()
98
98
99 users_data_total_inactive_count = User.query()\
99 users_data_total_inactive_count = User.query()\
100 .filter(User.username != User.DEFAULT_USER) \
100 .filter(User.username != User.DEFAULT_USER) \
101 .filter(User.active != true())\
101 .filter(User.active != true())\
102 .count()
102 .count()
103
103
104 # json generate
104 # json generate
105 base_q = User.query().filter(User.username != User.DEFAULT_USER)
105 base_q = User.query().filter(User.username != User.DEFAULT_USER)
106 base_inactive_q = base_q.filter(User.active != true())
106 base_inactive_q = base_q.filter(User.active != true())
107
107
108 if search_q:
108 if search_q:
109 like_expression = u'%{}%'.format(safe_unicode(search_q))
109 like_expression = '%{}%'.format(safe_unicode(search_q))
110 base_q = base_q.filter(or_(
110 base_q = base_q.filter(or_(
111 User.username.ilike(like_expression),
111 User.username.ilike(like_expression),
112 User._email.ilike(like_expression),
112 User._email.ilike(like_expression),
113 User.name.ilike(like_expression),
113 User.name.ilike(like_expression),
114 User.lastname.ilike(like_expression),
114 User.lastname.ilike(like_expression),
115 ))
115 ))
116 base_inactive_q = base_q.filter(User.active != true())
116 base_inactive_q = base_q.filter(User.active != true())
117
117
118 users_data_total_filtered_count = base_q.count()
118 users_data_total_filtered_count = base_q.count()
119 users_data_total_filtered_inactive_count = base_inactive_q.count()
119 users_data_total_filtered_inactive_count = base_inactive_q.count()
120
120
121 sort_col = getattr(User, order_by, None)
121 sort_col = getattr(User, order_by, None)
122 if sort_col:
122 if sort_col:
123 if order_dir == 'asc':
123 if order_dir == 'asc':
124 # handle null values properly to order by NULL last
124 # handle null values properly to order by NULL last
125 if order_by in ['last_activity']:
125 if order_by in ['last_activity']:
126 sort_col = coalesce(sort_col, datetime.date.max)
126 sort_col = coalesce(sort_col, datetime.date.max)
127 sort_col = sort_col.asc()
127 sort_col = sort_col.asc()
128 else:
128 else:
129 # handle null values properly to order by NULL last
129 # handle null values properly to order by NULL last
130 if order_by in ['last_activity']:
130 if order_by in ['last_activity']:
131 sort_col = coalesce(sort_col, datetime.date.min)
131 sort_col = coalesce(sort_col, datetime.date.min)
132 sort_col = sort_col.desc()
132 sort_col = sort_col.desc()
133
133
134 base_q = base_q.order_by(sort_col)
134 base_q = base_q.order_by(sort_col)
135 base_q = base_q.offset(start).limit(limit)
135 base_q = base_q.offset(start).limit(limit)
136
136
137 users_list = base_q.all()
137 users_list = base_q.all()
138
138
139 users_data = []
139 users_data = []
140 for user in users_list:
140 for user in users_list:
141 users_data.append({
141 users_data.append({
142 "username": h.gravatar_with_user(self.request, user.username),
142 "username": h.gravatar_with_user(self.request, user.username),
143 "email": user.email,
143 "email": user.email,
144 "first_name": user.first_name,
144 "first_name": user.first_name,
145 "last_name": user.last_name,
145 "last_name": user.last_name,
146 "last_login": h.format_date(user.last_login),
146 "last_login": h.format_date(user.last_login),
147 "last_activity": h.format_date(user.last_activity),
147 "last_activity": h.format_date(user.last_activity),
148 "active": h.bool2icon(user.active),
148 "active": h.bool2icon(user.active),
149 "active_raw": user.active,
149 "active_raw": user.active,
150 "admin": h.bool2icon(user.admin),
150 "admin": h.bool2icon(user.admin),
151 "extern_type": user.extern_type,
151 "extern_type": user.extern_type,
152 "extern_name": user.extern_name,
152 "extern_name": user.extern_name,
153 "action": user_actions(user.user_id, user.username),
153 "action": user_actions(user.user_id, user.username),
154 })
154 })
155 data = ({
155 data = ({
156 'draw': draw,
156 'draw': draw,
157 'data': users_data,
157 'data': users_data,
158 'recordsTotal': users_data_total_count,
158 'recordsTotal': users_data_total_count,
159 'recordsFiltered': users_data_total_filtered_count,
159 'recordsFiltered': users_data_total_filtered_count,
160 'recordsTotalInactive': users_data_total_inactive_count,
160 'recordsTotalInactive': users_data_total_inactive_count,
161 'recordsFilteredInactive': users_data_total_filtered_inactive_count
161 'recordsFilteredInactive': users_data_total_filtered_inactive_count
162 })
162 })
163
163
164 return data
164 return data
165
165
166 def _set_personal_repo_group_template_vars(self, c_obj):
166 def _set_personal_repo_group_template_vars(self, c_obj):
167 DummyUser = AttributeDict({
167 DummyUser = AttributeDict({
168 'username': '${username}',
168 'username': '${username}',
169 'user_id': '${user_id}',
169 'user_id': '${user_id}',
170 })
170 })
171 c_obj.default_create_repo_group = RepoGroupModel() \
171 c_obj.default_create_repo_group = RepoGroupModel() \
172 .get_default_create_personal_repo_group()
172 .get_default_create_personal_repo_group()
173 c_obj.personal_repo_group_name = RepoGroupModel() \
173 c_obj.personal_repo_group_name = RepoGroupModel() \
174 .get_personal_group_name(DummyUser)
174 .get_personal_group_name(DummyUser)
175
175
176 @LoginRequired()
176 @LoginRequired()
177 @HasPermissionAllDecorator('hg.admin')
177 @HasPermissionAllDecorator('hg.admin')
178 def users_new(self):
178 def users_new(self):
179 _ = self.request.translate
179 _ = self.request.translate
180 c = self.load_default_context()
180 c = self.load_default_context()
181 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
181 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
182 self._set_personal_repo_group_template_vars(c)
182 self._set_personal_repo_group_template_vars(c)
183 return self._get_template_context(c)
183 return self._get_template_context(c)
184
184
185 @LoginRequired()
185 @LoginRequired()
186 @HasPermissionAllDecorator('hg.admin')
186 @HasPermissionAllDecorator('hg.admin')
187 @CSRFRequired()
187 @CSRFRequired()
188 def users_create(self):
188 def users_create(self):
189 _ = self.request.translate
189 _ = self.request.translate
190 c = self.load_default_context()
190 c = self.load_default_context()
191 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
191 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
192 user_model = UserModel()
192 user_model = UserModel()
193 user_form = UserForm(self.request.translate)()
193 user_form = UserForm(self.request.translate)()
194 try:
194 try:
195 form_result = user_form.to_python(dict(self.request.POST))
195 form_result = user_form.to_python(dict(self.request.POST))
196 user = user_model.create(form_result)
196 user = user_model.create(form_result)
197 Session().flush()
197 Session().flush()
198 creation_data = user.get_api_data()
198 creation_data = user.get_api_data()
199 username = form_result['username']
199 username = form_result['username']
200
200
201 audit_logger.store_web(
201 audit_logger.store_web(
202 'user.create', action_data={'data': creation_data},
202 'user.create', action_data={'data': creation_data},
203 user=c.rhodecode_user)
203 user=c.rhodecode_user)
204
204
205 user_link = h.link_to(
205 user_link = h.link_to(
206 h.escape(username),
206 h.escape(username),
207 h.route_path('user_edit', user_id=user.user_id))
207 h.route_path('user_edit', user_id=user.user_id))
208 h.flash(h.literal(_('Created user %(user_link)s')
208 h.flash(h.literal(_('Created user %(user_link)s')
209 % {'user_link': user_link}), category='success')
209 % {'user_link': user_link}), category='success')
210 Session().commit()
210 Session().commit()
211 except formencode.Invalid as errors:
211 except formencode.Invalid as errors:
212 self._set_personal_repo_group_template_vars(c)
212 self._set_personal_repo_group_template_vars(c)
213 data = render(
213 data = render(
214 'rhodecode:templates/admin/users/user_add.mako',
214 'rhodecode:templates/admin/users/user_add.mako',
215 self._get_template_context(c), self.request)
215 self._get_template_context(c), self.request)
216 html = formencode.htmlfill.render(
216 html = formencode.htmlfill.render(
217 data,
217 data,
218 defaults=errors.value,
218 defaults=errors.value,
219 errors=errors.error_dict or {},
219 errors=errors.unpack_errors() or {},
220 prefix_error=False,
220 prefix_error=False,
221 encoding="UTF-8",
221 encoding="UTF-8",
222 force_defaults=False
222 force_defaults=False
223 )
223 )
224 return Response(html)
224 return Response(html)
225 except UserCreationError as e:
225 except UserCreationError as e:
226 h.flash(e, 'error')
226 h.flash(safe_unicode(e), 'error')
227 except Exception:
227 except Exception:
228 log.exception("Exception creation of user")
228 log.exception("Exception creation of user")
229 h.flash(_('Error occurred during creation of user %s')
229 h.flash(_('Error occurred during creation of user %s')
230 % self.request.POST.get('username'), category='error')
230 % self.request.POST.get('username'), category='error')
231 raise HTTPFound(h.route_path('users'))
231 raise HTTPFound(h.route_path('users'))
232
232
233
233
234 class UsersView(UserAppView):
234 class UsersView(UserAppView):
235 ALLOW_SCOPED_TOKENS = False
235 ALLOW_SCOPED_TOKENS = False
236 """
236 """
237 This view has alternative version inside EE, if modified please take a look
237 This view has alternative version inside EE, if modified please take a look
238 in there as well.
238 in there as well.
239 """
239 """
240
240
241 def get_auth_plugins(self):
241 def get_auth_plugins(self):
242 valid_plugins = []
242 valid_plugins = []
243 authn_registry = get_authn_registry(self.request.registry)
243 authn_registry = get_authn_registry(self.request.registry)
244 for plugin in authn_registry.get_plugins_for_authentication():
244 for plugin in authn_registry.get_plugins_for_authentication():
245 if isinstance(plugin, RhodeCodeExternalAuthPlugin):
245 if isinstance(plugin, RhodeCodeExternalAuthPlugin):
246 valid_plugins.append(plugin)
246 valid_plugins.append(plugin)
247 elif plugin.name == 'rhodecode':
247 elif plugin.name == 'rhodecode':
248 valid_plugins.append(plugin)
248 valid_plugins.append(plugin)
249
249
250 # extend our choices if user has set a bound plugin which isn't enabled at the
250 # extend our choices if user has set a bound plugin which isn't enabled at the
251 # moment
251 # moment
252 extern_type = self.db_user.extern_type
252 extern_type = self.db_user.extern_type
253 if extern_type not in [x.uid for x in valid_plugins]:
253 if extern_type not in [x.uid for x in valid_plugins]:
254 try:
254 try:
255 plugin = authn_registry.get_plugin_by_uid(extern_type)
255 plugin = authn_registry.get_plugin_by_uid(extern_type)
256 if plugin:
256 if plugin:
257 valid_plugins.append(plugin)
257 valid_plugins.append(plugin)
258
258
259 except Exception:
259 except Exception:
260 log.exception(
260 log.exception(
261 'Could not extend user plugins with `{}`'.format(extern_type))
261 'Could not extend user plugins with `{}`'.format(extern_type))
262 return valid_plugins
262 return valid_plugins
263
263
264 def load_default_context(self):
264 def load_default_context(self):
265 req = self.request
265 req = self.request
266
266
267 c = self._get_local_tmpl_context()
267 c = self._get_local_tmpl_context()
268 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
268 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
269 c.allowed_languages = [
269 c.allowed_languages = [
270 ('en', 'English (en)'),
270 ('en', 'English (en)'),
271 ('de', 'German (de)'),
271 ('de', 'German (de)'),
272 ('fr', 'French (fr)'),
272 ('fr', 'French (fr)'),
273 ('it', 'Italian (it)'),
273 ('it', 'Italian (it)'),
274 ('ja', 'Japanese (ja)'),
274 ('ja', 'Japanese (ja)'),
275 ('pl', 'Polish (pl)'),
275 ('pl', 'Polish (pl)'),
276 ('pt', 'Portuguese (pt)'),
276 ('pt', 'Portuguese (pt)'),
277 ('ru', 'Russian (ru)'),
277 ('ru', 'Russian (ru)'),
278 ('zh', 'Chinese (zh)'),
278 ('zh', 'Chinese (zh)'),
279 ]
279 ]
280
280
281 c.allowed_extern_types = [
281 c.allowed_extern_types = [
282 (x.uid, x.get_display_name()) for x in self.get_auth_plugins()
282 (x.uid, x.get_display_name()) for x in self.get_auth_plugins()
283 ]
283 ]
284 perms = req.registry.settings.get('available_permissions')
284 perms = req.registry.settings.get('available_permissions')
285 if not perms:
285 if not perms:
286 # inject info about available permissions
286 # inject info about available permissions
287 auth.set_available_permissions(req.registry.settings)
287 auth.set_available_permissions(req.registry.settings)
288
288
289 c.available_permissions = req.registry.settings['available_permissions']
289 c.available_permissions = req.registry.settings['available_permissions']
290 PermissionModel().set_global_permission_choices(
290 PermissionModel().set_global_permission_choices(
291 c, gettext_translator=req.translate)
291 c, gettext_translator=req.translate)
292
292
293 return c
293 return c
294
294
295 @LoginRequired()
295 @LoginRequired()
296 @HasPermissionAllDecorator('hg.admin')
296 @HasPermissionAllDecorator('hg.admin')
297 @CSRFRequired()
297 @CSRFRequired()
298 def user_update(self):
298 def user_update(self):
299 _ = self.request.translate
299 _ = self.request.translate
300 c = self.load_default_context()
300 c = self.load_default_context()
301
301
302 user_id = self.db_user_id
302 user_id = self.db_user_id
303 c.user = self.db_user
303 c.user = self.db_user
304
304
305 c.active = 'profile'
305 c.active = 'profile'
306 c.extern_type = c.user.extern_type
306 c.extern_type = c.user.extern_type
307 c.extern_name = c.user.extern_name
307 c.extern_name = c.user.extern_name
308 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
308 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
309 available_languages = [x[0] for x in c.allowed_languages]
309 available_languages = [x[0] for x in c.allowed_languages]
310 _form = UserForm(self.request.translate, edit=True,
310 _form = UserForm(self.request.translate, edit=True,
311 available_languages=available_languages,
311 available_languages=available_languages,
312 old_data={'user_id': user_id,
312 old_data={'user_id': user_id,
313 'email': c.user.email})()
313 'email': c.user.email})()
314
314
315 c.edit_mode = self.request.POST.get('edit') == '1'
315 c.edit_mode = self.request.POST.get('edit') == '1'
316 form_result = {}
316 form_result = {}
317 old_values = c.user.get_api_data()
317 old_values = c.user.get_api_data()
318 try:
318 try:
319 form_result = _form.to_python(dict(self.request.POST))
319 form_result = _form.to_python(dict(self.request.POST))
320 skip_attrs = ['extern_name']
320 skip_attrs = ['extern_name']
321 # TODO: plugin should define if username can be updated
321 # TODO: plugin should define if username can be updated
322
322
323 if c.extern_type != "rhodecode" and not c.edit_mode:
323 if c.extern_type != "rhodecode" and not c.edit_mode:
324 # forbid updating username for external accounts
324 # forbid updating username for external accounts
325 skip_attrs.append('username')
325 skip_attrs.append('username')
326
326
327 UserModel().update_user(
327 UserModel().update_user(
328 user_id, skip_attrs=skip_attrs, **form_result)
328 user_id, skip_attrs=skip_attrs, **form_result)
329
329
330 audit_logger.store_web(
330 audit_logger.store_web(
331 'user.edit', action_data={'old_data': old_values},
331 'user.edit', action_data={'old_data': old_values},
332 user=c.rhodecode_user)
332 user=c.rhodecode_user)
333
333
334 Session().commit()
334 Session().commit()
335 h.flash(_('User updated successfully'), category='success')
335 h.flash(_('User updated successfully'), category='success')
336 except formencode.Invalid as errors:
336 except formencode.Invalid as errors:
337 data = render(
337 data = render(
338 'rhodecode:templates/admin/users/user_edit.mako',
338 'rhodecode:templates/admin/users/user_edit.mako',
339 self._get_template_context(c), self.request)
339 self._get_template_context(c), self.request)
340 html = formencode.htmlfill.render(
340 html = formencode.htmlfill.render(
341 data,
341 data,
342 defaults=errors.value,
342 defaults=errors.value,
343 errors=errors.error_dict or {},
343 errors=errors.unpack_errors() or {},
344 prefix_error=False,
344 prefix_error=False,
345 encoding="UTF-8",
345 encoding="UTF-8",
346 force_defaults=False
346 force_defaults=False
347 )
347 )
348 return Response(html)
348 return Response(html)
349 except UserCreationError as e:
349 except UserCreationError as e:
350 h.flash(e, 'error')
350 h.flash(safe_unicode(e), 'error')
351 except Exception:
351 except Exception:
352 log.exception("Exception updating user")
352 log.exception("Exception updating user")
353 h.flash(_('Error occurred during update of user %s')
353 h.flash(_('Error occurred during update of user %s')
354 % form_result.get('username'), category='error')
354 % form_result.get('username'), category='error')
355 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
355 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
356
356
357 @LoginRequired()
357 @LoginRequired()
358 @HasPermissionAllDecorator('hg.admin')
358 @HasPermissionAllDecorator('hg.admin')
359 @CSRFRequired()
359 @CSRFRequired()
360 def user_delete(self):
360 def user_delete(self):
361 _ = self.request.translate
361 _ = self.request.translate
362 c = self.load_default_context()
362 c = self.load_default_context()
363 c.user = self.db_user
363 c.user = self.db_user
364
364
365 _repos = c.user.repositories
365 _repos = c.user.repositories
366 _repo_groups = c.user.repository_groups
366 _repo_groups = c.user.repository_groups
367 _user_groups = c.user.user_groups
367 _user_groups = c.user.user_groups
368 _pull_requests = c.user.user_pull_requests
368 _pull_requests = c.user.user_pull_requests
369 _artifacts = c.user.artifacts
369 _artifacts = c.user.artifacts
370
370
371 handle_repos = None
371 handle_repos = None
372 handle_repo_groups = None
372 handle_repo_groups = None
373 handle_user_groups = None
373 handle_user_groups = None
374 handle_pull_requests = None
374 handle_pull_requests = None
375 handle_artifacts = None
375 handle_artifacts = None
376
376
377 # calls for flash of handle based on handle case detach or delete
377 # calls for flash of handle based on handle case detach or delete
378 def set_handle_flash_repos():
378 def set_handle_flash_repos():
379 handle = handle_repos
379 handle = handle_repos
380 if handle == 'detach':
380 if handle == 'detach':
381 h.flash(_('Detached %s repositories') % len(_repos),
381 h.flash(_('Detached %s repositories') % len(_repos),
382 category='success')
382 category='success')
383 elif handle == 'delete':
383 elif handle == 'delete':
384 h.flash(_('Deleted %s repositories') % len(_repos),
384 h.flash(_('Deleted %s repositories') % len(_repos),
385 category='success')
385 category='success')
386
386
387 def set_handle_flash_repo_groups():
387 def set_handle_flash_repo_groups():
388 handle = handle_repo_groups
388 handle = handle_repo_groups
389 if handle == 'detach':
389 if handle == 'detach':
390 h.flash(_('Detached %s repository groups') % len(_repo_groups),
390 h.flash(_('Detached %s repository groups') % len(_repo_groups),
391 category='success')
391 category='success')
392 elif handle == 'delete':
392 elif handle == 'delete':
393 h.flash(_('Deleted %s repository groups') % len(_repo_groups),
393 h.flash(_('Deleted %s repository groups') % len(_repo_groups),
394 category='success')
394 category='success')
395
395
396 def set_handle_flash_user_groups():
396 def set_handle_flash_user_groups():
397 handle = handle_user_groups
397 handle = handle_user_groups
398 if handle == 'detach':
398 if handle == 'detach':
399 h.flash(_('Detached %s user groups') % len(_user_groups),
399 h.flash(_('Detached %s user groups') % len(_user_groups),
400 category='success')
400 category='success')
401 elif handle == 'delete':
401 elif handle == 'delete':
402 h.flash(_('Deleted %s user groups') % len(_user_groups),
402 h.flash(_('Deleted %s user groups') % len(_user_groups),
403 category='success')
403 category='success')
404
404
405 def set_handle_flash_pull_requests():
405 def set_handle_flash_pull_requests():
406 handle = handle_pull_requests
406 handle = handle_pull_requests
407 if handle == 'detach':
407 if handle == 'detach':
408 h.flash(_('Detached %s pull requests') % len(_pull_requests),
408 h.flash(_('Detached %s pull requests') % len(_pull_requests),
409 category='success')
409 category='success')
410 elif handle == 'delete':
410 elif handle == 'delete':
411 h.flash(_('Deleted %s pull requests') % len(_pull_requests),
411 h.flash(_('Deleted %s pull requests') % len(_pull_requests),
412 category='success')
412 category='success')
413
413
414 def set_handle_flash_artifacts():
414 def set_handle_flash_artifacts():
415 handle = handle_artifacts
415 handle = handle_artifacts
416 if handle == 'detach':
416 if handle == 'detach':
417 h.flash(_('Detached %s artifacts') % len(_artifacts),
417 h.flash(_('Detached %s artifacts') % len(_artifacts),
418 category='success')
418 category='success')
419 elif handle == 'delete':
419 elif handle == 'delete':
420 h.flash(_('Deleted %s artifacts') % len(_artifacts),
420 h.flash(_('Deleted %s artifacts') % len(_artifacts),
421 category='success')
421 category='success')
422
422
423 handle_user = User.get_first_super_admin()
423 handle_user = User.get_first_super_admin()
424 handle_user_id = safe_int(self.request.POST.get('detach_user_id'))
424 handle_user_id = safe_int(self.request.POST.get('detach_user_id'))
425 if handle_user_id:
425 if handle_user_id:
426 # NOTE(marcink): we get new owner for objects...
426 # NOTE(marcink): we get new owner for objects...
427 handle_user = User.get_or_404(handle_user_id)
427 handle_user = User.get_or_404(handle_user_id)
428
428
429 if _repos and self.request.POST.get('user_repos'):
429 if _repos and self.request.POST.get('user_repos'):
430 handle_repos = self.request.POST['user_repos']
430 handle_repos = self.request.POST['user_repos']
431
431
432 if _repo_groups and self.request.POST.get('user_repo_groups'):
432 if _repo_groups and self.request.POST.get('user_repo_groups'):
433 handle_repo_groups = self.request.POST['user_repo_groups']
433 handle_repo_groups = self.request.POST['user_repo_groups']
434
434
435 if _user_groups and self.request.POST.get('user_user_groups'):
435 if _user_groups and self.request.POST.get('user_user_groups'):
436 handle_user_groups = self.request.POST['user_user_groups']
436 handle_user_groups = self.request.POST['user_user_groups']
437
437
438 if _pull_requests and self.request.POST.get('user_pull_requests'):
438 if _pull_requests and self.request.POST.get('user_pull_requests'):
439 handle_pull_requests = self.request.POST['user_pull_requests']
439 handle_pull_requests = self.request.POST['user_pull_requests']
440
440
441 if _artifacts and self.request.POST.get('user_artifacts'):
441 if _artifacts and self.request.POST.get('user_artifacts'):
442 handle_artifacts = self.request.POST['user_artifacts']
442 handle_artifacts = self.request.POST['user_artifacts']
443
443
444 old_values = c.user.get_api_data()
444 old_values = c.user.get_api_data()
445
445
446 try:
446 try:
447
447
448 UserModel().delete(
448 UserModel().delete(
449 c.user,
449 c.user,
450 handle_repos=handle_repos,
450 handle_repos=handle_repos,
451 handle_repo_groups=handle_repo_groups,
451 handle_repo_groups=handle_repo_groups,
452 handle_user_groups=handle_user_groups,
452 handle_user_groups=handle_user_groups,
453 handle_pull_requests=handle_pull_requests,
453 handle_pull_requests=handle_pull_requests,
454 handle_artifacts=handle_artifacts,
454 handle_artifacts=handle_artifacts,
455 handle_new_owner=handle_user
455 handle_new_owner=handle_user
456 )
456 )
457
457
458 audit_logger.store_web(
458 audit_logger.store_web(
459 'user.delete', action_data={'old_data': old_values},
459 'user.delete', action_data={'old_data': old_values},
460 user=c.rhodecode_user)
460 user=c.rhodecode_user)
461
461
462 Session().commit()
462 Session().commit()
463 set_handle_flash_repos()
463 set_handle_flash_repos()
464 set_handle_flash_repo_groups()
464 set_handle_flash_repo_groups()
465 set_handle_flash_user_groups()
465 set_handle_flash_user_groups()
466 set_handle_flash_pull_requests()
466 set_handle_flash_pull_requests()
467 set_handle_flash_artifacts()
467 set_handle_flash_artifacts()
468 username = h.escape(old_values['username'])
468 username = h.escape(old_values['username'])
469 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
469 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
470 except (UserOwnsReposException, UserOwnsRepoGroupsException,
470 except (UserOwnsReposException, UserOwnsRepoGroupsException,
471 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
471 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
472 UserOwnsArtifactsException, DefaultUserException) as e:
472 UserOwnsArtifactsException, DefaultUserException) as e:
473 h.flash(e, category='warning')
473 h.flash(e, category='warning')
474 except Exception:
474 except Exception:
475 log.exception("Exception during deletion of user")
475 log.exception("Exception during deletion of user")
476 h.flash(_('An error occurred during deletion of user'),
476 h.flash(_('An error occurred during deletion of user'),
477 category='error')
477 category='error')
478 raise HTTPFound(h.route_path('users'))
478 raise HTTPFound(h.route_path('users'))
479
479
480 @LoginRequired()
480 @LoginRequired()
481 @HasPermissionAllDecorator('hg.admin')
481 @HasPermissionAllDecorator('hg.admin')
482 def user_edit(self):
482 def user_edit(self):
483 _ = self.request.translate
483 _ = self.request.translate
484 c = self.load_default_context()
484 c = self.load_default_context()
485 c.user = self.db_user
485 c.user = self.db_user
486
486
487 c.active = 'profile'
487 c.active = 'profile'
488 c.extern_type = c.user.extern_type
488 c.extern_type = c.user.extern_type
489 c.extern_name = c.user.extern_name
489 c.extern_name = c.user.extern_name
490 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
490 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
491 c.edit_mode = self.request.GET.get('edit') == '1'
491 c.edit_mode = self.request.GET.get('edit') == '1'
492
492
493 defaults = c.user.get_dict()
493 defaults = c.user.get_dict()
494 defaults.update({'language': c.user.user_data.get('language')})
494 defaults.update({'language': c.user.user_data.get('language')})
495
495
496 data = render(
496 data = render(
497 'rhodecode:templates/admin/users/user_edit.mako',
497 'rhodecode:templates/admin/users/user_edit.mako',
498 self._get_template_context(c), self.request)
498 self._get_template_context(c), self.request)
499 html = formencode.htmlfill.render(
499 html = formencode.htmlfill.render(
500 data,
500 data,
501 defaults=defaults,
501 defaults=defaults,
502 encoding="UTF-8",
502 encoding="UTF-8",
503 force_defaults=False
503 force_defaults=False
504 )
504 )
505 return Response(html)
505 return Response(html)
506
506
507 @LoginRequired()
507 @LoginRequired()
508 @HasPermissionAllDecorator('hg.admin')
508 @HasPermissionAllDecorator('hg.admin')
509 def user_edit_advanced(self):
509 def user_edit_advanced(self):
510 _ = self.request.translate
510 _ = self.request.translate
511 c = self.load_default_context()
511 c = self.load_default_context()
512
512
513 user_id = self.db_user_id
513 user_id = self.db_user_id
514 c.user = self.db_user
514 c.user = self.db_user
515
515
516 c.detach_user = User.get_first_super_admin()
516 c.detach_user = User.get_first_super_admin()
517 detach_user_id = safe_int(self.request.GET.get('detach_user_id'))
517 detach_user_id = safe_int(self.request.GET.get('detach_user_id'))
518 if detach_user_id:
518 if detach_user_id:
519 c.detach_user = User.get_or_404(detach_user_id)
519 c.detach_user = User.get_or_404(detach_user_id)
520
520
521 c.active = 'advanced'
521 c.active = 'advanced'
522 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
522 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
523 c.personal_repo_group_name = RepoGroupModel()\
523 c.personal_repo_group_name = RepoGroupModel()\
524 .get_personal_group_name(c.user)
524 .get_personal_group_name(c.user)
525
525
526 c.user_to_review_rules = sorted(
526 c.user_to_review_rules = sorted(
527 (x.user for x in c.user.user_review_rules),
527 (x.user for x in c.user.user_review_rules),
528 key=lambda u: u.username.lower())
528 key=lambda u: u.username.lower())
529
529
530 defaults = c.user.get_dict()
530 defaults = c.user.get_dict()
531
531
532 # Interim workaround if the user participated on any pull requests as a
532 # Interim workaround if the user participated on any pull requests as a
533 # reviewer.
533 # reviewer.
534 has_review = len(c.user.reviewer_pull_requests)
534 has_review = len(c.user.reviewer_pull_requests)
535 c.can_delete_user = not has_review
535 c.can_delete_user = not has_review
536 c.can_delete_user_message = ''
536 c.can_delete_user_message = ''
537 inactive_link = h.link_to(
537 inactive_link = h.link_to(
538 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
538 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
539 if has_review == 1:
539 if has_review == 1:
540 c.can_delete_user_message = h.literal(_(
540 c.can_delete_user_message = h.literal(_(
541 'The user participates as reviewer in {} pull request and '
541 'The user participates as reviewer in {} pull request and '
542 'cannot be deleted. \nYou can set the user to '
542 'cannot be deleted. \nYou can set the user to '
543 '"{}" instead of deleting it.').format(
543 '"{}" instead of deleting it.').format(
544 has_review, inactive_link))
544 has_review, inactive_link))
545 elif has_review:
545 elif has_review:
546 c.can_delete_user_message = h.literal(_(
546 c.can_delete_user_message = h.literal(_(
547 'The user participates as reviewer in {} pull requests and '
547 'The user participates as reviewer in {} pull requests and '
548 'cannot be deleted. \nYou can set the user to '
548 'cannot be deleted. \nYou can set the user to '
549 '"{}" instead of deleting it.').format(
549 '"{}" instead of deleting it.').format(
550 has_review, inactive_link))
550 has_review, inactive_link))
551
551
552 data = render(
552 data = render(
553 'rhodecode:templates/admin/users/user_edit.mako',
553 'rhodecode:templates/admin/users/user_edit.mako',
554 self._get_template_context(c), self.request)
554 self._get_template_context(c), self.request)
555 html = formencode.htmlfill.render(
555 html = formencode.htmlfill.render(
556 data,
556 data,
557 defaults=defaults,
557 defaults=defaults,
558 encoding="UTF-8",
558 encoding="UTF-8",
559 force_defaults=False
559 force_defaults=False
560 )
560 )
561 return Response(html)
561 return Response(html)
562
562
563 @LoginRequired()
563 @LoginRequired()
564 @HasPermissionAllDecorator('hg.admin')
564 @HasPermissionAllDecorator('hg.admin')
565 def user_edit_global_perms(self):
565 def user_edit_global_perms(self):
566 _ = self.request.translate
566 _ = self.request.translate
567 c = self.load_default_context()
567 c = self.load_default_context()
568 c.user = self.db_user
568 c.user = self.db_user
569
569
570 c.active = 'global_perms'
570 c.active = 'global_perms'
571
571
572 c.default_user = User.get_default_user()
572 c.default_user = User.get_default_user()
573 defaults = c.user.get_dict()
573 defaults = c.user.get_dict()
574 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
574 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
575 defaults.update(c.default_user.get_default_perms())
575 defaults.update(c.default_user.get_default_perms())
576 defaults.update(c.user.get_default_perms())
576 defaults.update(c.user.get_default_perms())
577
577
578 data = render(
578 data = render(
579 'rhodecode:templates/admin/users/user_edit.mako',
579 'rhodecode:templates/admin/users/user_edit.mako',
580 self._get_template_context(c), self.request)
580 self._get_template_context(c), self.request)
581 html = formencode.htmlfill.render(
581 html = formencode.htmlfill.render(
582 data,
582 data,
583 defaults=defaults,
583 defaults=defaults,
584 encoding="UTF-8",
584 encoding="UTF-8",
585 force_defaults=False
585 force_defaults=False
586 )
586 )
587 return Response(html)
587 return Response(html)
588
588
589 @LoginRequired()
589 @LoginRequired()
590 @HasPermissionAllDecorator('hg.admin')
590 @HasPermissionAllDecorator('hg.admin')
591 @CSRFRequired()
591 @CSRFRequired()
592 def user_edit_global_perms_update(self):
592 def user_edit_global_perms_update(self):
593 _ = self.request.translate
593 _ = self.request.translate
594 c = self.load_default_context()
594 c = self.load_default_context()
595
595
596 user_id = self.db_user_id
596 user_id = self.db_user_id
597 c.user = self.db_user
597 c.user = self.db_user
598
598
599 c.active = 'global_perms'
599 c.active = 'global_perms'
600 try:
600 try:
601 # first stage that verifies the checkbox
601 # first stage that verifies the checkbox
602 _form = UserIndividualPermissionsForm(self.request.translate)
602 _form = UserIndividualPermissionsForm(self.request.translate)
603 form_result = _form.to_python(dict(self.request.POST))
603 form_result = _form.to_python(dict(self.request.POST))
604 inherit_perms = form_result['inherit_default_permissions']
604 inherit_perms = form_result['inherit_default_permissions']
605 c.user.inherit_default_permissions = inherit_perms
605 c.user.inherit_default_permissions = inherit_perms
606 Session().add(c.user)
606 Session().add(c.user)
607
607
608 if not inherit_perms:
608 if not inherit_perms:
609 # only update the individual ones if we un check the flag
609 # only update the individual ones if we un check the flag
610 _form = UserPermissionsForm(
610 _form = UserPermissionsForm(
611 self.request.translate,
611 self.request.translate,
612 [x[0] for x in c.repo_create_choices],
612 [x[0] for x in c.repo_create_choices],
613 [x[0] for x in c.repo_create_on_write_choices],
613 [x[0] for x in c.repo_create_on_write_choices],
614 [x[0] for x in c.repo_group_create_choices],
614 [x[0] for x in c.repo_group_create_choices],
615 [x[0] for x in c.user_group_create_choices],
615 [x[0] for x in c.user_group_create_choices],
616 [x[0] for x in c.fork_choices],
616 [x[0] for x in c.fork_choices],
617 [x[0] for x in c.inherit_default_permission_choices])()
617 [x[0] for x in c.inherit_default_permission_choices])()
618
618
619 form_result = _form.to_python(dict(self.request.POST))
619 form_result = _form.to_python(dict(self.request.POST))
620 form_result.update({'perm_user_id': c.user.user_id})
620 form_result.update({'perm_user_id': c.user.user_id})
621
621
622 PermissionModel().update_user_permissions(form_result)
622 PermissionModel().update_user_permissions(form_result)
623
623
624 # TODO(marcink): implement global permissions
624 # TODO(marcink): implement global permissions
625 # audit_log.store_web('user.edit.permissions')
625 # audit_log.store_web('user.edit.permissions')
626
626
627 Session().commit()
627 Session().commit()
628
628
629 h.flash(_('User global permissions updated successfully'),
629 h.flash(_('User global permissions updated successfully'),
630 category='success')
630 category='success')
631
631
632 except formencode.Invalid as errors:
632 except formencode.Invalid as errors:
633 data = render(
633 data = render(
634 'rhodecode:templates/admin/users/user_edit.mako',
634 'rhodecode:templates/admin/users/user_edit.mako',
635 self._get_template_context(c), self.request)
635 self._get_template_context(c), self.request)
636 html = formencode.htmlfill.render(
636 html = formencode.htmlfill.render(
637 data,
637 data,
638 defaults=errors.value,
638 defaults=errors.value,
639 errors=errors.error_dict or {},
639 errors=errors.unpack_errors() or {},
640 prefix_error=False,
640 prefix_error=False,
641 encoding="UTF-8",
641 encoding="UTF-8",
642 force_defaults=False
642 force_defaults=False
643 )
643 )
644 return Response(html)
644 return Response(html)
645 except Exception:
645 except Exception:
646 log.exception("Exception during permissions saving")
646 log.exception("Exception during permissions saving")
647 h.flash(_('An error occurred during permissions saving'),
647 h.flash(_('An error occurred during permissions saving'),
648 category='error')
648 category='error')
649
649
650 affected_user_ids = [user_id]
650 affected_user_ids = [user_id]
651 PermissionModel().trigger_permission_flush(affected_user_ids)
651 PermissionModel().trigger_permission_flush(affected_user_ids)
652 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
652 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
653
653
654 @LoginRequired()
654 @LoginRequired()
655 @HasPermissionAllDecorator('hg.admin')
655 @HasPermissionAllDecorator('hg.admin')
656 @CSRFRequired()
656 @CSRFRequired()
657 def user_enable_force_password_reset(self):
657 def user_enable_force_password_reset(self):
658 _ = self.request.translate
658 _ = self.request.translate
659 c = self.load_default_context()
659 c = self.load_default_context()
660
660
661 user_id = self.db_user_id
661 user_id = self.db_user_id
662 c.user = self.db_user
662 c.user = self.db_user
663
663
664 try:
664 try:
665 c.user.update_userdata(force_password_change=True)
665 c.user.update_userdata(force_password_change=True)
666
666
667 msg = _('Force password change enabled for user')
667 msg = _('Force password change enabled for user')
668 audit_logger.store_web('user.edit.password_reset.enabled',
668 audit_logger.store_web('user.edit.password_reset.enabled',
669 user=c.rhodecode_user)
669 user=c.rhodecode_user)
670
670
671 Session().commit()
671 Session().commit()
672 h.flash(msg, category='success')
672 h.flash(msg, category='success')
673 except Exception:
673 except Exception:
674 log.exception("Exception during password reset for user")
674 log.exception("Exception during password reset for user")
675 h.flash(_('An error occurred during password reset for user'),
675 h.flash(_('An error occurred during password reset for user'),
676 category='error')
676 category='error')
677
677
678 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
678 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
679
679
680 @LoginRequired()
680 @LoginRequired()
681 @HasPermissionAllDecorator('hg.admin')
681 @HasPermissionAllDecorator('hg.admin')
682 @CSRFRequired()
682 @CSRFRequired()
683 def user_disable_force_password_reset(self):
683 def user_disable_force_password_reset(self):
684 _ = self.request.translate
684 _ = self.request.translate
685 c = self.load_default_context()
685 c = self.load_default_context()
686
686
687 user_id = self.db_user_id
687 user_id = self.db_user_id
688 c.user = self.db_user
688 c.user = self.db_user
689
689
690 try:
690 try:
691 c.user.update_userdata(force_password_change=False)
691 c.user.update_userdata(force_password_change=False)
692
692
693 msg = _('Force password change disabled for user')
693 msg = _('Force password change disabled for user')
694 audit_logger.store_web(
694 audit_logger.store_web(
695 'user.edit.password_reset.disabled',
695 'user.edit.password_reset.disabled',
696 user=c.rhodecode_user)
696 user=c.rhodecode_user)
697
697
698 Session().commit()
698 Session().commit()
699 h.flash(msg, category='success')
699 h.flash(msg, category='success')
700 except Exception:
700 except Exception:
701 log.exception("Exception during password reset for user")
701 log.exception("Exception during password reset for user")
702 h.flash(_('An error occurred during password reset for user'),
702 h.flash(_('An error occurred during password reset for user'),
703 category='error')
703 category='error')
704
704
705 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
705 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
706
706
707 @LoginRequired()
707 @LoginRequired()
708 @HasPermissionAllDecorator('hg.admin')
708 @HasPermissionAllDecorator('hg.admin')
709 @CSRFRequired()
709 @CSRFRequired()
710 def user_notice_dismiss(self):
710 def user_notice_dismiss(self):
711 _ = self.request.translate
711 _ = self.request.translate
712 c = self.load_default_context()
712 c = self.load_default_context()
713
713
714 user_id = self.db_user_id
714 user_id = self.db_user_id
715 c.user = self.db_user
715 c.user = self.db_user
716 user_notice_id = safe_int(self.request.POST.get('notice_id'))
716 user_notice_id = safe_int(self.request.POST.get('notice_id'))
717 notice = UserNotice().query()\
717 notice = UserNotice().query()\
718 .filter(UserNotice.user_id == user_id)\
718 .filter(UserNotice.user_id == user_id)\
719 .filter(UserNotice.user_notice_id == user_notice_id)\
719 .filter(UserNotice.user_notice_id == user_notice_id)\
720 .scalar()
720 .scalar()
721 read = False
721 read = False
722 if notice:
722 if notice:
723 notice.notice_read = True
723 notice.notice_read = True
724 Session().add(notice)
724 Session().add(notice)
725 Session().commit()
725 Session().commit()
726 read = True
726 read = True
727
727
728 return {'notice': user_notice_id, 'read': read}
728 return {'notice': user_notice_id, 'read': read}
729
729
730 @LoginRequired()
730 @LoginRequired()
731 @HasPermissionAllDecorator('hg.admin')
731 @HasPermissionAllDecorator('hg.admin')
732 @CSRFRequired()
732 @CSRFRequired()
733 def user_create_personal_repo_group(self):
733 def user_create_personal_repo_group(self):
734 """
734 """
735 Create personal repository group for this user
735 Create personal repository group for this user
736 """
736 """
737 from rhodecode.model.repo_group import RepoGroupModel
737 from rhodecode.model.repo_group import RepoGroupModel
738
738
739 _ = self.request.translate
739 _ = self.request.translate
740 c = self.load_default_context()
740 c = self.load_default_context()
741
741
742 user_id = self.db_user_id
742 user_id = self.db_user_id
743 c.user = self.db_user
743 c.user = self.db_user
744
744
745 personal_repo_group = RepoGroup.get_user_personal_repo_group(
745 personal_repo_group = RepoGroup.get_user_personal_repo_group(
746 c.user.user_id)
746 c.user.user_id)
747 if personal_repo_group:
747 if personal_repo_group:
748 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
748 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
749
749
750 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
750 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
751 named_personal_group = RepoGroup.get_by_group_name(
751 named_personal_group = RepoGroup.get_by_group_name(
752 personal_repo_group_name)
752 personal_repo_group_name)
753 try:
753 try:
754
754
755 if named_personal_group and named_personal_group.user_id == c.user.user_id:
755 if named_personal_group and named_personal_group.user_id == c.user.user_id:
756 # migrate the same named group, and mark it as personal
756 # migrate the same named group, and mark it as personal
757 named_personal_group.personal = True
757 named_personal_group.personal = True
758 Session().add(named_personal_group)
758 Session().add(named_personal_group)
759 Session().commit()
759 Session().commit()
760 msg = _('Linked repository group `%s` as personal' % (
760 msg = _('Linked repository group `%s` as personal' % (
761 personal_repo_group_name,))
761 personal_repo_group_name,))
762 h.flash(msg, category='success')
762 h.flash(msg, category='success')
763 elif not named_personal_group:
763 elif not named_personal_group:
764 RepoGroupModel().create_personal_repo_group(c.user)
764 RepoGroupModel().create_personal_repo_group(c.user)
765
765
766 msg = _('Created repository group `%s`' % (
766 msg = _('Created repository group `%s`' % (
767 personal_repo_group_name,))
767 personal_repo_group_name,))
768 h.flash(msg, category='success')
768 h.flash(msg, category='success')
769 else:
769 else:
770 msg = _('Repository group `%s` is already taken' % (
770 msg = _('Repository group `%s` is already taken' % (
771 personal_repo_group_name,))
771 personal_repo_group_name,))
772 h.flash(msg, category='warning')
772 h.flash(msg, category='warning')
773 except Exception:
773 except Exception:
774 log.exception("Exception during repository group creation")
774 log.exception("Exception during repository group creation")
775 msg = _(
775 msg = _(
776 'An error occurred during repository group creation for user')
776 'An error occurred during repository group creation for user')
777 h.flash(msg, category='error')
777 h.flash(msg, category='error')
778 Session().rollback()
778 Session().rollback()
779
779
780 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
780 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
781
781
782 @LoginRequired()
782 @LoginRequired()
783 @HasPermissionAllDecorator('hg.admin')
783 @HasPermissionAllDecorator('hg.admin')
784 def auth_tokens(self):
784 def auth_tokens(self):
785 _ = self.request.translate
785 _ = self.request.translate
786 c = self.load_default_context()
786 c = self.load_default_context()
787 c.user = self.db_user
787 c.user = self.db_user
788
788
789 c.active = 'auth_tokens'
789 c.active = 'auth_tokens'
790
790
791 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
791 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
792 c.role_values = [
792 c.role_values = [
793 (x, AuthTokenModel.cls._get_role_name(x))
793 (x, AuthTokenModel.cls._get_role_name(x))
794 for x in AuthTokenModel.cls.ROLES]
794 for x in AuthTokenModel.cls.ROLES]
795 c.role_options = [(c.role_values, _("Role"))]
795 c.role_options = [(c.role_values, _("Role"))]
796 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
796 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
797 c.user.user_id, show_expired=True)
797 c.user.user_id, show_expired=True)
798 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
798 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
799 return self._get_template_context(c)
799 return self._get_template_context(c)
800
800
801 @LoginRequired()
801 @LoginRequired()
802 @HasPermissionAllDecorator('hg.admin')
802 @HasPermissionAllDecorator('hg.admin')
803 def auth_tokens_view(self):
803 def auth_tokens_view(self):
804 _ = self.request.translate
804 _ = self.request.translate
805 c = self.load_default_context()
805 c = self.load_default_context()
806 c.user = self.db_user
806 c.user = self.db_user
807
807
808 auth_token_id = self.request.POST.get('auth_token_id')
808 auth_token_id = self.request.POST.get('auth_token_id')
809
809
810 if auth_token_id:
810 if auth_token_id:
811 token = UserApiKeys.get_or_404(auth_token_id)
811 token = UserApiKeys.get_or_404(auth_token_id)
812
812
813 return {
813 return {
814 'auth_token': token.api_key
814 'auth_token': token.api_key
815 }
815 }
816
816
817 def maybe_attach_token_scope(self, token):
817 def maybe_attach_token_scope(self, token):
818 # implemented in EE edition
818 # implemented in EE edition
819 pass
819 pass
820
820
821 @LoginRequired()
821 @LoginRequired()
822 @HasPermissionAllDecorator('hg.admin')
822 @HasPermissionAllDecorator('hg.admin')
823 @CSRFRequired()
823 @CSRFRequired()
824 def auth_tokens_add(self):
824 def auth_tokens_add(self):
825 _ = self.request.translate
825 _ = self.request.translate
826 c = self.load_default_context()
826 c = self.load_default_context()
827
827
828 user_id = self.db_user_id
828 user_id = self.db_user_id
829 c.user = self.db_user
829 c.user = self.db_user
830
830
831 user_data = c.user.get_api_data()
831 user_data = c.user.get_api_data()
832 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
832 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
833 description = self.request.POST.get('description')
833 description = self.request.POST.get('description')
834 role = self.request.POST.get('role')
834 role = self.request.POST.get('role')
835
835
836 token = UserModel().add_auth_token(
836 token = UserModel().add_auth_token(
837 user=c.user.user_id,
837 user=c.user.user_id,
838 lifetime_minutes=lifetime, role=role, description=description,
838 lifetime_minutes=lifetime, role=role, description=description,
839 scope_callback=self.maybe_attach_token_scope)
839 scope_callback=self.maybe_attach_token_scope)
840 token_data = token.get_api_data()
840 token_data = token.get_api_data()
841
841
842 audit_logger.store_web(
842 audit_logger.store_web(
843 'user.edit.token.add', action_data={
843 'user.edit.token.add', action_data={
844 'data': {'token': token_data, 'user': user_data}},
844 'data': {'token': token_data, 'user': user_data}},
845 user=self._rhodecode_user, )
845 user=self._rhodecode_user, )
846 Session().commit()
846 Session().commit()
847
847
848 h.flash(_("Auth token successfully created"), category='success')
848 h.flash(_("Auth token successfully created"), category='success')
849 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
849 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
850
850
851 @LoginRequired()
851 @LoginRequired()
852 @HasPermissionAllDecorator('hg.admin')
852 @HasPermissionAllDecorator('hg.admin')
853 @CSRFRequired()
853 @CSRFRequired()
854 def auth_tokens_delete(self):
854 def auth_tokens_delete(self):
855 _ = self.request.translate
855 _ = self.request.translate
856 c = self.load_default_context()
856 c = self.load_default_context()
857
857
858 user_id = self.db_user_id
858 user_id = self.db_user_id
859 c.user = self.db_user
859 c.user = self.db_user
860
860
861 user_data = c.user.get_api_data()
861 user_data = c.user.get_api_data()
862
862
863 del_auth_token = self.request.POST.get('del_auth_token')
863 del_auth_token = self.request.POST.get('del_auth_token')
864
864
865 if del_auth_token:
865 if del_auth_token:
866 token = UserApiKeys.get_or_404(del_auth_token)
866 token = UserApiKeys.get_or_404(del_auth_token)
867 token_data = token.get_api_data()
867 token_data = token.get_api_data()
868
868
869 AuthTokenModel().delete(del_auth_token, c.user.user_id)
869 AuthTokenModel().delete(del_auth_token, c.user.user_id)
870 audit_logger.store_web(
870 audit_logger.store_web(
871 'user.edit.token.delete', action_data={
871 'user.edit.token.delete', action_data={
872 'data': {'token': token_data, 'user': user_data}},
872 'data': {'token': token_data, 'user': user_data}},
873 user=self._rhodecode_user,)
873 user=self._rhodecode_user,)
874 Session().commit()
874 Session().commit()
875 h.flash(_("Auth token successfully deleted"), category='success')
875 h.flash(_("Auth token successfully deleted"), category='success')
876
876
877 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
877 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
878
878
879 @LoginRequired()
879 @LoginRequired()
880 @HasPermissionAllDecorator('hg.admin')
880 @HasPermissionAllDecorator('hg.admin')
881 def ssh_keys(self):
881 def ssh_keys(self):
882 _ = self.request.translate
882 _ = self.request.translate
883 c = self.load_default_context()
883 c = self.load_default_context()
884 c.user = self.db_user
884 c.user = self.db_user
885
885
886 c.active = 'ssh_keys'
886 c.active = 'ssh_keys'
887 c.default_key = self.request.GET.get('default_key')
887 c.default_key = self.request.GET.get('default_key')
888 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
888 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
889 return self._get_template_context(c)
889 return self._get_template_context(c)
890
890
891 @LoginRequired()
891 @LoginRequired()
892 @HasPermissionAllDecorator('hg.admin')
892 @HasPermissionAllDecorator('hg.admin')
893 def ssh_keys_generate_keypair(self):
893 def ssh_keys_generate_keypair(self):
894 _ = self.request.translate
894 _ = self.request.translate
895 c = self.load_default_context()
895 c = self.load_default_context()
896
896
897 c.user = self.db_user
897 c.user = self.db_user
898
898
899 c.active = 'ssh_keys_generate'
899 c.active = 'ssh_keys_generate'
900 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
900 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
901 private_format = self.request.GET.get('private_format') \
901 private_format = self.request.GET.get('private_format') \
902 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
902 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
903 c.private, c.public = SshKeyModel().generate_keypair(
903 c.private, c.public = SshKeyModel().generate_keypair(
904 comment=comment, private_format=private_format)
904 comment=comment, private_format=private_format)
905
905
906 return self._get_template_context(c)
906 return self._get_template_context(c)
907
907
908 @LoginRequired()
908 @LoginRequired()
909 @HasPermissionAllDecorator('hg.admin')
909 @HasPermissionAllDecorator('hg.admin')
910 @CSRFRequired()
910 @CSRFRequired()
911 def ssh_keys_add(self):
911 def ssh_keys_add(self):
912 _ = self.request.translate
912 _ = self.request.translate
913 c = self.load_default_context()
913 c = self.load_default_context()
914
914
915 user_id = self.db_user_id
915 user_id = self.db_user_id
916 c.user = self.db_user
916 c.user = self.db_user
917
917
918 user_data = c.user.get_api_data()
918 user_data = c.user.get_api_data()
919 key_data = self.request.POST.get('key_data')
919 key_data = self.request.POST.get('key_data')
920 description = self.request.POST.get('description')
920 description = self.request.POST.get('description')
921
921
922 fingerprint = 'unknown'
922 fingerprint = 'unknown'
923 try:
923 try:
924 if not key_data:
924 if not key_data:
925 raise ValueError('Please add a valid public key')
925 raise ValueError('Please add a valid public key')
926
926
927 key = SshKeyModel().parse_key(key_data.strip())
927 key = SshKeyModel().parse_key(key_data.strip())
928 fingerprint = key.hash_md5()
928 fingerprint = key.hash_md5()
929
929
930 ssh_key = SshKeyModel().create(
930 ssh_key = SshKeyModel().create(
931 c.user.user_id, fingerprint, key.keydata, description)
931 c.user.user_id, fingerprint, key.keydata, description)
932 ssh_key_data = ssh_key.get_api_data()
932 ssh_key_data = ssh_key.get_api_data()
933
933
934 audit_logger.store_web(
934 audit_logger.store_web(
935 'user.edit.ssh_key.add', action_data={
935 'user.edit.ssh_key.add', action_data={
936 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
936 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
937 user=self._rhodecode_user, )
937 user=self._rhodecode_user, )
938 Session().commit()
938 Session().commit()
939
939
940 # Trigger an event on change of keys.
940 # Trigger an event on change of keys.
941 trigger(SshKeyFileChangeEvent(), self.request.registry)
941 trigger(SshKeyFileChangeEvent(), self.request.registry)
942
942
943 h.flash(_("Ssh Key successfully created"), category='success')
943 h.flash(_("Ssh Key successfully created"), category='success')
944
944
945 except IntegrityError:
945 except IntegrityError:
946 log.exception("Exception during ssh key saving")
946 log.exception("Exception during ssh key saving")
947 err = 'Such key with fingerprint `{}` already exists, ' \
947 err = 'Such key with fingerprint `{}` already exists, ' \
948 'please use a different one'.format(fingerprint)
948 'please use a different one'.format(fingerprint)
949 h.flash(_('An error occurred during ssh key saving: {}').format(err),
949 h.flash(_('An error occurred during ssh key saving: {}').format(err),
950 category='error')
950 category='error')
951 except Exception as e:
951 except Exception as e:
952 log.exception("Exception during ssh key saving")
952 log.exception("Exception during ssh key saving")
953 h.flash(_('An error occurred during ssh key saving: {}').format(e),
953 h.flash(_('An error occurred during ssh key saving: {}').format(e),
954 category='error')
954 category='error')
955
955
956 return HTTPFound(
956 return HTTPFound(
957 h.route_path('edit_user_ssh_keys', user_id=user_id))
957 h.route_path('edit_user_ssh_keys', user_id=user_id))
958
958
959 @LoginRequired()
959 @LoginRequired()
960 @HasPermissionAllDecorator('hg.admin')
960 @HasPermissionAllDecorator('hg.admin')
961 @CSRFRequired()
961 @CSRFRequired()
962 def ssh_keys_delete(self):
962 def ssh_keys_delete(self):
963 _ = self.request.translate
963 _ = self.request.translate
964 c = self.load_default_context()
964 c = self.load_default_context()
965
965
966 user_id = self.db_user_id
966 user_id = self.db_user_id
967 c.user = self.db_user
967 c.user = self.db_user
968
968
969 user_data = c.user.get_api_data()
969 user_data = c.user.get_api_data()
970
970
971 del_ssh_key = self.request.POST.get('del_ssh_key')
971 del_ssh_key = self.request.POST.get('del_ssh_key')
972
972
973 if del_ssh_key:
973 if del_ssh_key:
974 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
974 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
975 ssh_key_data = ssh_key.get_api_data()
975 ssh_key_data = ssh_key.get_api_data()
976
976
977 SshKeyModel().delete(del_ssh_key, c.user.user_id)
977 SshKeyModel().delete(del_ssh_key, c.user.user_id)
978 audit_logger.store_web(
978 audit_logger.store_web(
979 'user.edit.ssh_key.delete', action_data={
979 'user.edit.ssh_key.delete', action_data={
980 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
980 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
981 user=self._rhodecode_user,)
981 user=self._rhodecode_user,)
982 Session().commit()
982 Session().commit()
983 # Trigger an event on change of keys.
983 # Trigger an event on change of keys.
984 trigger(SshKeyFileChangeEvent(), self.request.registry)
984 trigger(SshKeyFileChangeEvent(), self.request.registry)
985 h.flash(_("Ssh key successfully deleted"), category='success')
985 h.flash(_("Ssh key successfully deleted"), category='success')
986
986
987 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
987 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
988
988
989 @LoginRequired()
989 @LoginRequired()
990 @HasPermissionAllDecorator('hg.admin')
990 @HasPermissionAllDecorator('hg.admin')
991 def emails(self):
991 def emails(self):
992 _ = self.request.translate
992 _ = self.request.translate
993 c = self.load_default_context()
993 c = self.load_default_context()
994 c.user = self.db_user
994 c.user = self.db_user
995
995
996 c.active = 'emails'
996 c.active = 'emails'
997 c.user_email_map = UserEmailMap.query() \
997 c.user_email_map = UserEmailMap.query() \
998 .filter(UserEmailMap.user == c.user).all()
998 .filter(UserEmailMap.user == c.user).all()
999
999
1000 return self._get_template_context(c)
1000 return self._get_template_context(c)
1001
1001
1002 @LoginRequired()
1002 @LoginRequired()
1003 @HasPermissionAllDecorator('hg.admin')
1003 @HasPermissionAllDecorator('hg.admin')
1004 @CSRFRequired()
1004 @CSRFRequired()
1005 def emails_add(self):
1005 def emails_add(self):
1006 _ = self.request.translate
1006 _ = self.request.translate
1007 c = self.load_default_context()
1007 c = self.load_default_context()
1008
1008
1009 user_id = self.db_user_id
1009 user_id = self.db_user_id
1010 c.user = self.db_user
1010 c.user = self.db_user
1011
1011
1012 email = self.request.POST.get('new_email')
1012 email = self.request.POST.get('new_email')
1013 user_data = c.user.get_api_data()
1013 user_data = c.user.get_api_data()
1014 try:
1014 try:
1015
1015
1016 form = UserExtraEmailForm(self.request.translate)()
1016 form = UserExtraEmailForm(self.request.translate)()
1017 data = form.to_python({'email': email})
1017 data = form.to_python({'email': email})
1018 email = data['email']
1018 email = data['email']
1019
1019
1020 UserModel().add_extra_email(c.user.user_id, email)
1020 UserModel().add_extra_email(c.user.user_id, email)
1021 audit_logger.store_web(
1021 audit_logger.store_web(
1022 'user.edit.email.add',
1022 'user.edit.email.add',
1023 action_data={'email': email, 'user': user_data},
1023 action_data={'email': email, 'user': user_data},
1024 user=self._rhodecode_user)
1024 user=self._rhodecode_user)
1025 Session().commit()
1025 Session().commit()
1026 h.flash(_("Added new email address `%s` for user account") % email,
1026 h.flash(_("Added new email address `%s` for user account") % email,
1027 category='success')
1027 category='success')
1028 except formencode.Invalid as error:
1028 except formencode.Invalid as error:
1029 h.flash(h.escape(error.error_dict['email']), category='error')
1029 msg = error.unpack_errors()['email']
1030 h.flash(h.escape(msg), category='error')
1030 except IntegrityError:
1031 except IntegrityError:
1031 log.warning("Email %s already exists", email)
1032 log.warning("Email %s already exists", email)
1032 h.flash(_('Email `{}` is already registered for another user.').format(email),
1033 h.flash(_('Email `{}` is already registered for another user.').format(email),
1033 category='error')
1034 category='error')
1034 except Exception:
1035 except Exception:
1035 log.exception("Exception during email saving")
1036 log.exception("Exception during email saving")
1036 h.flash(_('An error occurred during email saving'),
1037 h.flash(_('An error occurred during email saving'),
1037 category='error')
1038 category='error')
1038 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1039 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1039
1040
1040 @LoginRequired()
1041 @LoginRequired()
1041 @HasPermissionAllDecorator('hg.admin')
1042 @HasPermissionAllDecorator('hg.admin')
1042 @CSRFRequired()
1043 @CSRFRequired()
1043 def emails_delete(self):
1044 def emails_delete(self):
1044 _ = self.request.translate
1045 _ = self.request.translate
1045 c = self.load_default_context()
1046 c = self.load_default_context()
1046
1047
1047 user_id = self.db_user_id
1048 user_id = self.db_user_id
1048 c.user = self.db_user
1049 c.user = self.db_user
1049
1050
1050 email_id = self.request.POST.get('del_email_id')
1051 email_id = self.request.POST.get('del_email_id')
1051 user_model = UserModel()
1052 user_model = UserModel()
1052
1053
1053 email = UserEmailMap.query().get(email_id).email
1054 email = UserEmailMap.query().get(email_id).email
1054 user_data = c.user.get_api_data()
1055 user_data = c.user.get_api_data()
1055 user_model.delete_extra_email(c.user.user_id, email_id)
1056 user_model.delete_extra_email(c.user.user_id, email_id)
1056 audit_logger.store_web(
1057 audit_logger.store_web(
1057 'user.edit.email.delete',
1058 'user.edit.email.delete',
1058 action_data={'email': email, 'user': user_data},
1059 action_data={'email': email, 'user': user_data},
1059 user=self._rhodecode_user)
1060 user=self._rhodecode_user)
1060 Session().commit()
1061 Session().commit()
1061 h.flash(_("Removed email address from user account"),
1062 h.flash(_("Removed email address from user account"),
1062 category='success')
1063 category='success')
1063 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1064 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1064
1065
1065 @LoginRequired()
1066 @LoginRequired()
1066 @HasPermissionAllDecorator('hg.admin')
1067 @HasPermissionAllDecorator('hg.admin')
1067 def ips(self):
1068 def ips(self):
1068 _ = self.request.translate
1069 _ = self.request.translate
1069 c = self.load_default_context()
1070 c = self.load_default_context()
1070 c.user = self.db_user
1071 c.user = self.db_user
1071
1072
1072 c.active = 'ips'
1073 c.active = 'ips'
1073 c.user_ip_map = UserIpMap.query() \
1074 c.user_ip_map = UserIpMap.query() \
1074 .filter(UserIpMap.user == c.user).all()
1075 .filter(UserIpMap.user == c.user).all()
1075
1076
1076 c.inherit_default_ips = c.user.inherit_default_permissions
1077 c.inherit_default_ips = c.user.inherit_default_permissions
1077 c.default_user_ip_map = UserIpMap.query() \
1078 c.default_user_ip_map = UserIpMap.query() \
1078 .filter(UserIpMap.user == User.get_default_user()).all()
1079 .filter(UserIpMap.user == User.get_default_user()).all()
1079
1080
1080 return self._get_template_context(c)
1081 return self._get_template_context(c)
1081
1082
1082 @LoginRequired()
1083 @LoginRequired()
1083 @HasPermissionAllDecorator('hg.admin')
1084 @HasPermissionAllDecorator('hg.admin')
1084 @CSRFRequired()
1085 @CSRFRequired()
1085 # NOTE(marcink): this view is allowed for default users, as we can
1086 # NOTE(marcink): this view is allowed for default users, as we can
1086 # edit their IP white list
1087 # edit their IP white list
1087 def ips_add(self):
1088 def ips_add(self):
1088 _ = self.request.translate
1089 _ = self.request.translate
1089 c = self.load_default_context()
1090 c = self.load_default_context()
1090
1091
1091 user_id = self.db_user_id
1092 user_id = self.db_user_id
1092 c.user = self.db_user
1093 c.user = self.db_user
1093
1094
1094 user_model = UserModel()
1095 user_model = UserModel()
1095 desc = self.request.POST.get('description')
1096 desc = self.request.POST.get('description')
1096 try:
1097 try:
1097 ip_list = user_model.parse_ip_range(
1098 ip_list = user_model.parse_ip_range(
1098 self.request.POST.get('new_ip'))
1099 self.request.POST.get('new_ip'))
1099 except Exception as e:
1100 except Exception as e:
1100 ip_list = []
1101 ip_list = []
1101 log.exception("Exception during ip saving")
1102 log.exception("Exception during ip saving")
1102 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1103 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1103 category='error')
1104 category='error')
1104 added = []
1105 added = []
1105 user_data = c.user.get_api_data()
1106 user_data = c.user.get_api_data()
1106 for ip in ip_list:
1107 for ip in ip_list:
1107 try:
1108 try:
1108 form = UserExtraIpForm(self.request.translate)()
1109 form = UserExtraIpForm(self.request.translate)()
1109 data = form.to_python({'ip': ip})
1110 data = form.to_python({'ip': ip})
1110 ip = data['ip']
1111 ip = data['ip']
1111
1112
1112 user_model.add_extra_ip(c.user.user_id, ip, desc)
1113 user_model.add_extra_ip(c.user.user_id, ip, desc)
1113 audit_logger.store_web(
1114 audit_logger.store_web(
1114 'user.edit.ip.add',
1115 'user.edit.ip.add',
1115 action_data={'ip': ip, 'user': user_data},
1116 action_data={'ip': ip, 'user': user_data},
1116 user=self._rhodecode_user)
1117 user=self._rhodecode_user)
1117 Session().commit()
1118 Session().commit()
1118 added.append(ip)
1119 added.append(ip)
1119 except formencode.Invalid as error:
1120 except formencode.Invalid as error:
1120 msg = error.error_dict['ip']
1121 msg = error.unpack_errors()['ip']
1121 h.flash(msg, category='error')
1122 h.flash(msg, category='error')
1122 except Exception:
1123 except Exception:
1123 log.exception("Exception during ip saving")
1124 log.exception("Exception during ip saving")
1124 h.flash(_('An error occurred during ip saving'),
1125 h.flash(_('An error occurred during ip saving'),
1125 category='error')
1126 category='error')
1126 if added:
1127 if added:
1127 h.flash(
1128 h.flash(
1128 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1129 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1129 category='success')
1130 category='success')
1130 if 'default_user' in self.request.POST:
1131 if 'default_user' in self.request.POST:
1131 # case for editing global IP list we do it for 'DEFAULT' user
1132 # case for editing global IP list we do it for 'DEFAULT' user
1132 raise HTTPFound(h.route_path('admin_permissions_ips'))
1133 raise HTTPFound(h.route_path('admin_permissions_ips'))
1133 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1134 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1134
1135
1135 @LoginRequired()
1136 @LoginRequired()
1136 @HasPermissionAllDecorator('hg.admin')
1137 @HasPermissionAllDecorator('hg.admin')
1137 @CSRFRequired()
1138 @CSRFRequired()
1138 # NOTE(marcink): this view is allowed for default users, as we can
1139 # NOTE(marcink): this view is allowed for default users, as we can
1139 # edit their IP white list
1140 # edit their IP white list
1140 def ips_delete(self):
1141 def ips_delete(self):
1141 _ = self.request.translate
1142 _ = self.request.translate
1142 c = self.load_default_context()
1143 c = self.load_default_context()
1143
1144
1144 user_id = self.db_user_id
1145 user_id = self.db_user_id
1145 c.user = self.db_user
1146 c.user = self.db_user
1146
1147
1147 ip_id = self.request.POST.get('del_ip_id')
1148 ip_id = self.request.POST.get('del_ip_id')
1148 user_model = UserModel()
1149 user_model = UserModel()
1149 user_data = c.user.get_api_data()
1150 user_data = c.user.get_api_data()
1150 ip = UserIpMap.query().get(ip_id).ip_addr
1151 ip = UserIpMap.query().get(ip_id).ip_addr
1151 user_model.delete_extra_ip(c.user.user_id, ip_id)
1152 user_model.delete_extra_ip(c.user.user_id, ip_id)
1152 audit_logger.store_web(
1153 audit_logger.store_web(
1153 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1154 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1154 user=self._rhodecode_user)
1155 user=self._rhodecode_user)
1155 Session().commit()
1156 Session().commit()
1156 h.flash(_("Removed ip address from user whitelist"), category='success')
1157 h.flash(_("Removed ip address from user whitelist"), category='success')
1157
1158
1158 if 'default_user' in self.request.POST:
1159 if 'default_user' in self.request.POST:
1159 # case for editing global IP list we do it for 'DEFAULT' user
1160 # case for editing global IP list we do it for 'DEFAULT' user
1160 raise HTTPFound(h.route_path('admin_permissions_ips'))
1161 raise HTTPFound(h.route_path('admin_permissions_ips'))
1161 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1162 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1162
1163
1163 @LoginRequired()
1164 @LoginRequired()
1164 @HasPermissionAllDecorator('hg.admin')
1165 @HasPermissionAllDecorator('hg.admin')
1165 def groups_management(self):
1166 def groups_management(self):
1166 c = self.load_default_context()
1167 c = self.load_default_context()
1167 c.user = self.db_user
1168 c.user = self.db_user
1168 c.data = c.user.group_member
1169 c.data = c.user.group_member
1169
1170
1170 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1171 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1171 for group in c.user.group_member]
1172 for group in c.user.group_member]
1172 c.groups = ext_json.str_json(groups)
1173 c.groups = ext_json.str_json(groups)
1173 c.active = 'groups'
1174 c.active = 'groups'
1174
1175
1175 return self._get_template_context(c)
1176 return self._get_template_context(c)
1176
1177
1177 @LoginRequired()
1178 @LoginRequired()
1178 @HasPermissionAllDecorator('hg.admin')
1179 @HasPermissionAllDecorator('hg.admin')
1179 @CSRFRequired()
1180 @CSRFRequired()
1180 def groups_management_updates(self):
1181 def groups_management_updates(self):
1181 _ = self.request.translate
1182 _ = self.request.translate
1182 c = self.load_default_context()
1183 c = self.load_default_context()
1183
1184
1184 user_id = self.db_user_id
1185 user_id = self.db_user_id
1185 c.user = self.db_user
1186 c.user = self.db_user
1186
1187
1187 user_groups = set(self.request.POST.getall('users_group_id'))
1188 user_groups = set(self.request.POST.getall('users_group_id'))
1188 user_groups_objects = []
1189 user_groups_objects = []
1189
1190
1190 for ugid in user_groups:
1191 for ugid in user_groups:
1191 user_groups_objects.append(
1192 user_groups_objects.append(
1192 UserGroupModel().get_group(safe_int(ugid)))
1193 UserGroupModel().get_group(safe_int(ugid)))
1193 user_group_model = UserGroupModel()
1194 user_group_model = UserGroupModel()
1194 added_to_groups, removed_from_groups = \
1195 added_to_groups, removed_from_groups = \
1195 user_group_model.change_groups(c.user, user_groups_objects)
1196 user_group_model.change_groups(c.user, user_groups_objects)
1196
1197
1197 user_data = c.user.get_api_data()
1198 user_data = c.user.get_api_data()
1198 for user_group_id in added_to_groups:
1199 for user_group_id in added_to_groups:
1199 user_group = UserGroup.get(user_group_id)
1200 user_group = UserGroup.get(user_group_id)
1200 old_values = user_group.get_api_data()
1201 old_values = user_group.get_api_data()
1201 audit_logger.store_web(
1202 audit_logger.store_web(
1202 'user_group.edit.member.add',
1203 'user_group.edit.member.add',
1203 action_data={'user': user_data, 'old_data': old_values},
1204 action_data={'user': user_data, 'old_data': old_values},
1204 user=self._rhodecode_user)
1205 user=self._rhodecode_user)
1205
1206
1206 for user_group_id in removed_from_groups:
1207 for user_group_id in removed_from_groups:
1207 user_group = UserGroup.get(user_group_id)
1208 user_group = UserGroup.get(user_group_id)
1208 old_values = user_group.get_api_data()
1209 old_values = user_group.get_api_data()
1209 audit_logger.store_web(
1210 audit_logger.store_web(
1210 'user_group.edit.member.delete',
1211 'user_group.edit.member.delete',
1211 action_data={'user': user_data, 'old_data': old_values},
1212 action_data={'user': user_data, 'old_data': old_values},
1212 user=self._rhodecode_user)
1213 user=self._rhodecode_user)
1213
1214
1214 Session().commit()
1215 Session().commit()
1215 c.active = 'user_groups_management'
1216 c.active = 'user_groups_management'
1216 h.flash(_("Groups successfully changed"), category='success')
1217 h.flash(_("Groups successfully changed"), category='success')
1217
1218
1218 return HTTPFound(h.route_path(
1219 return HTTPFound(h.route_path(
1219 'edit_user_groups_management', user_id=user_id))
1220 'edit_user_groups_management', user_id=user_id))
1220
1221
1221 @LoginRequired()
1222 @LoginRequired()
1222 @HasPermissionAllDecorator('hg.admin')
1223 @HasPermissionAllDecorator('hg.admin')
1223 def user_audit_logs(self):
1224 def user_audit_logs(self):
1224 _ = self.request.translate
1225 _ = self.request.translate
1225 c = self.load_default_context()
1226 c = self.load_default_context()
1226 c.user = self.db_user
1227 c.user = self.db_user
1227
1228
1228 c.active = 'audit'
1229 c.active = 'audit'
1229
1230
1230 p = safe_int(self.request.GET.get('page', 1), 1)
1231 p = safe_int(self.request.GET.get('page', 1), 1)
1231
1232
1232 filter_term = self.request.GET.get('filter')
1233 filter_term = self.request.GET.get('filter')
1233 user_log = UserModel().get_user_log(c.user, filter_term)
1234 user_log = UserModel().get_user_log(c.user, filter_term)
1234
1235
1235 def url_generator(page_num):
1236 def url_generator(page_num):
1236 query_params = {
1237 query_params = {
1237 'page': page_num
1238 'page': page_num
1238 }
1239 }
1239 if filter_term:
1240 if filter_term:
1240 query_params['filter'] = filter_term
1241 query_params['filter'] = filter_term
1241 return self.request.current_route_path(_query=query_params)
1242 return self.request.current_route_path(_query=query_params)
1242
1243
1243 c.audit_logs = SqlPage(
1244 c.audit_logs = SqlPage(
1244 user_log, page=p, items_per_page=10, url_maker=url_generator)
1245 user_log, page=p, items_per_page=10, url_maker=url_generator)
1245 c.filter_term = filter_term
1246 c.filter_term = filter_term
1246 return self._get_template_context(c)
1247 return self._get_template_context(c)
1247
1248
1248 @LoginRequired()
1249 @LoginRequired()
1249 @HasPermissionAllDecorator('hg.admin')
1250 @HasPermissionAllDecorator('hg.admin')
1250 def user_audit_logs_download(self):
1251 def user_audit_logs_download(self):
1251 _ = self.request.translate
1252 _ = self.request.translate
1252 c = self.load_default_context()
1253 c = self.load_default_context()
1253 c.user = self.db_user
1254 c.user = self.db_user
1254
1255
1255 user_log = UserModel().get_user_log(c.user, filter_term=None)
1256 user_log = UserModel().get_user_log(c.user, filter_term=None)
1256
1257
1257 audit_log_data = {}
1258 audit_log_data = {}
1258 for entry in user_log:
1259 for entry in user_log:
1259 audit_log_data[entry.user_log_id] = entry.get_dict()
1260 audit_log_data[entry.user_log_id] = entry.get_dict()
1260
1261
1261 response = Response(ext_json.formatted_str_json(audit_log_data))
1262 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'
1263 response.content_disposition = f'attachment; filename=user_{c.user.user_id}_audit_logs.json'
1263 response.content_type = 'application/json'
1264 response.content_type = 'application/json'
1264
1265
1265 return response
1266 return response
1266
1267
1267 @LoginRequired()
1268 @LoginRequired()
1268 @HasPermissionAllDecorator('hg.admin')
1269 @HasPermissionAllDecorator('hg.admin')
1269 def user_perms_summary(self):
1270 def user_perms_summary(self):
1270 _ = self.request.translate
1271 _ = self.request.translate
1271 c = self.load_default_context()
1272 c = self.load_default_context()
1272 c.user = self.db_user
1273 c.user = self.db_user
1273
1274
1274 c.active = 'perms_summary'
1275 c.active = 'perms_summary'
1275 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1276 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1276
1277
1277 return self._get_template_context(c)
1278 return self._get_template_context(c)
1278
1279
1279 @LoginRequired()
1280 @LoginRequired()
1280 @HasPermissionAllDecorator('hg.admin')
1281 @HasPermissionAllDecorator('hg.admin')
1281 def user_perms_summary_json(self):
1282 def user_perms_summary_json(self):
1282 self.load_default_context()
1283 self.load_default_context()
1283 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1284 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1284
1285
1285 return perm_user.permissions
1286 return perm_user.permissions
1286
1287
1287 @LoginRequired()
1288 @LoginRequired()
1288 @HasPermissionAllDecorator('hg.admin')
1289 @HasPermissionAllDecorator('hg.admin')
1289 def user_caches(self):
1290 def user_caches(self):
1290 _ = self.request.translate
1291 _ = self.request.translate
1291 c = self.load_default_context()
1292 c = self.load_default_context()
1292 c.user = self.db_user
1293 c.user = self.db_user
1293
1294
1294 c.active = 'caches'
1295 c.active = 'caches'
1295 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1296 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1296
1297
1297 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1298 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1298 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1299 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1299 c.backend = c.region.backend
1300 c.backend = c.region.backend
1300 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1301 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1301
1302
1302 return self._get_template_context(c)
1303 return self._get_template_context(c)
1303
1304
1304 @LoginRequired()
1305 @LoginRequired()
1305 @HasPermissionAllDecorator('hg.admin')
1306 @HasPermissionAllDecorator('hg.admin')
1306 @CSRFRequired()
1307 @CSRFRequired()
1307 def user_caches_update(self):
1308 def user_caches_update(self):
1308 _ = self.request.translate
1309 _ = self.request.translate
1309 c = self.load_default_context()
1310 c = self.load_default_context()
1310 c.user = self.db_user
1311 c.user = self.db_user
1311
1312
1312 c.active = 'caches'
1313 c.active = 'caches'
1313 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1314 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1314
1315
1315 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1316 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1316 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1317 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1317
1318
1318 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1319 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1319
1320
1320 return HTTPFound(h.route_path(
1321 return HTTPFound(h.route_path(
1321 'edit_user_caches', user_id=c.user.user_id))
1322 'edit_user_caches', user_id=c.user.user_id))
General Comments 0
You need to be logged in to leave comments. Login now