##// END OF EJS Templates
users: personal repo group template also can use first/last names for an user
marcink -
r3659:ccf1c60d default
parent child Browse files
Show More
@@ -1,1266 +1,1265 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode import events
31 from rhodecode import events
32 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
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
36 from rhodecode.model.db import true
37
37
38 from rhodecode.lib import audit_logger, rc_cache
38 from rhodecode.lib import audit_logger, rc_cache
39 from rhodecode.lib.exceptions import (
39 from rhodecode.lib.exceptions import (
40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserOwnsUserGroupsException, DefaultUserException)
41 UserOwnsUserGroupsException, DefaultUserException)
42 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.auth import (
43 from rhodecode.lib.auth import (
44 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
44 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
45 from rhodecode.lib import helpers as h
45 from rhodecode.lib import helpers as h
46 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
46 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
47 from rhodecode.model.auth_token import AuthTokenModel
47 from rhodecode.model.auth_token import AuthTokenModel
48 from rhodecode.model.forms import (
48 from rhodecode.model.forms import (
49 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
49 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
50 UserExtraEmailForm, UserExtraIpForm)
50 UserExtraEmailForm, UserExtraIpForm)
51 from rhodecode.model.permission import PermissionModel
51 from rhodecode.model.permission import PermissionModel
52 from rhodecode.model.repo_group import RepoGroupModel
52 from rhodecode.model.repo_group import RepoGroupModel
53 from rhodecode.model.ssh_key import SshKeyModel
53 from rhodecode.model.ssh_key import SshKeyModel
54 from rhodecode.model.user import UserModel
54 from rhodecode.model.user import UserModel
55 from rhodecode.model.user_group import UserGroupModel
55 from rhodecode.model.user_group import UserGroupModel
56 from rhodecode.model.db import (
56 from rhodecode.model.db import (
57 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
57 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
58 UserApiKeys, UserSshKeys, RepoGroup)
58 UserApiKeys, UserSshKeys, RepoGroup)
59 from rhodecode.model.meta import Session
59 from rhodecode.model.meta import Session
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63
63
64 class AdminUsersView(BaseAppView, DataGridAppView):
64 class AdminUsersView(BaseAppView, DataGridAppView):
65
65
66 def load_default_context(self):
66 def load_default_context(self):
67 c = self._get_local_tmpl_context()
67 c = self._get_local_tmpl_context()
68 return c
68 return c
69
69
70 @LoginRequired()
70 @LoginRequired()
71 @HasPermissionAllDecorator('hg.admin')
71 @HasPermissionAllDecorator('hg.admin')
72 @view_config(
72 @view_config(
73 route_name='users', request_method='GET',
73 route_name='users', request_method='GET',
74 renderer='rhodecode:templates/admin/users/users.mako')
74 renderer='rhodecode:templates/admin/users/users.mako')
75 def users_list(self):
75 def users_list(self):
76 c = self.load_default_context()
76 c = self.load_default_context()
77 return self._get_template_context(c)
77 return self._get_template_context(c)
78
78
79 @LoginRequired()
79 @LoginRequired()
80 @HasPermissionAllDecorator('hg.admin')
80 @HasPermissionAllDecorator('hg.admin')
81 @view_config(
81 @view_config(
82 # renderer defined below
82 # renderer defined below
83 route_name='users_data', request_method='GET',
83 route_name='users_data', request_method='GET',
84 renderer='json_ext', xhr=True)
84 renderer='json_ext', xhr=True)
85 def users_list_data(self):
85 def users_list_data(self):
86 self.load_default_context()
86 self.load_default_context()
87 column_map = {
87 column_map = {
88 'first_name': 'name',
88 'first_name': 'name',
89 'last_name': 'lastname',
89 'last_name': 'lastname',
90 }
90 }
91 draw, start, limit = self._extract_chunk(self.request)
91 draw, start, limit = self._extract_chunk(self.request)
92 search_q, order_by, order_dir = self._extract_ordering(
92 search_q, order_by, order_dir = self._extract_ordering(
93 self.request, column_map=column_map)
93 self.request, column_map=column_map)
94 _render = self.request.get_partial_renderer(
94 _render = self.request.get_partial_renderer(
95 'rhodecode:templates/data_table/_dt_elements.mako')
95 'rhodecode:templates/data_table/_dt_elements.mako')
96
96
97 def user_actions(user_id, username):
97 def user_actions(user_id, username):
98 return _render("user_actions", user_id, username)
98 return _render("user_actions", user_id, username)
99
99
100 users_data_total_count = User.query()\
100 users_data_total_count = User.query()\
101 .filter(User.username != User.DEFAULT_USER) \
101 .filter(User.username != User.DEFAULT_USER) \
102 .count()
102 .count()
103
103
104 users_data_total_inactive_count = User.query()\
104 users_data_total_inactive_count = User.query()\
105 .filter(User.username != User.DEFAULT_USER) \
105 .filter(User.username != User.DEFAULT_USER) \
106 .filter(User.active != true())\
106 .filter(User.active != true())\
107 .count()
107 .count()
108
108
109 # json generate
109 # json generate
110 base_q = User.query().filter(User.username != User.DEFAULT_USER)
110 base_q = User.query().filter(User.username != User.DEFAULT_USER)
111 base_inactive_q = base_q.filter(User.active != true())
111 base_inactive_q = base_q.filter(User.active != true())
112
112
113 if search_q:
113 if search_q:
114 like_expression = u'%{}%'.format(safe_unicode(search_q))
114 like_expression = u'%{}%'.format(safe_unicode(search_q))
115 base_q = base_q.filter(or_(
115 base_q = base_q.filter(or_(
116 User.username.ilike(like_expression),
116 User.username.ilike(like_expression),
117 User._email.ilike(like_expression),
117 User._email.ilike(like_expression),
118 User.name.ilike(like_expression),
118 User.name.ilike(like_expression),
119 User.lastname.ilike(like_expression),
119 User.lastname.ilike(like_expression),
120 ))
120 ))
121 base_inactive_q = base_q.filter(User.active != true())
121 base_inactive_q = base_q.filter(User.active != true())
122
122
123 users_data_total_filtered_count = base_q.count()
123 users_data_total_filtered_count = base_q.count()
124 users_data_total_filtered_inactive_count = base_inactive_q.count()
124 users_data_total_filtered_inactive_count = base_inactive_q.count()
125
125
126 sort_col = getattr(User, order_by, None)
126 sort_col = getattr(User, order_by, None)
127 if sort_col:
127 if sort_col:
128 if order_dir == 'asc':
128 if order_dir == 'asc':
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.max)
131 sort_col = coalesce(sort_col, datetime.date.max)
132 sort_col = sort_col.asc()
132 sort_col = sort_col.asc()
133 else:
133 else:
134 # handle null values properly to order by NULL last
134 # handle null values properly to order by NULL last
135 if order_by in ['last_activity']:
135 if order_by in ['last_activity']:
136 sort_col = coalesce(sort_col, datetime.date.min)
136 sort_col = coalesce(sort_col, datetime.date.min)
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 users_list = base_q.all()
142 users_list = base_q.all()
143
143
144 users_data = []
144 users_data = []
145 for user in users_list:
145 for user in users_list:
146 users_data.append({
146 users_data.append({
147 "username": h.gravatar_with_user(self.request, user.username),
147 "username": h.gravatar_with_user(self.request, user.username),
148 "email": user.email,
148 "email": user.email,
149 "first_name": user.first_name,
149 "first_name": user.first_name,
150 "last_name": user.last_name,
150 "last_name": user.last_name,
151 "last_login": h.format_date(user.last_login),
151 "last_login": h.format_date(user.last_login),
152 "last_activity": h.format_date(user.last_activity),
152 "last_activity": h.format_date(user.last_activity),
153 "active": h.bool2icon(user.active),
153 "active": h.bool2icon(user.active),
154 "active_raw": user.active,
154 "active_raw": user.active,
155 "admin": h.bool2icon(user.admin),
155 "admin": h.bool2icon(user.admin),
156 "extern_type": user.extern_type,
156 "extern_type": user.extern_type,
157 "extern_name": user.extern_name,
157 "extern_name": user.extern_name,
158 "action": user_actions(user.user_id, user.username),
158 "action": user_actions(user.user_id, user.username),
159 })
159 })
160 data = ({
160 data = ({
161 'draw': draw,
161 'draw': draw,
162 'data': users_data,
162 'data': users_data,
163 'recordsTotal': users_data_total_count,
163 'recordsTotal': users_data_total_count,
164 'recordsFiltered': users_data_total_filtered_count,
164 'recordsFiltered': users_data_total_filtered_count,
165 'recordsTotalInactive': users_data_total_inactive_count,
165 'recordsTotalInactive': users_data_total_inactive_count,
166 'recordsFilteredInactive': users_data_total_filtered_inactive_count
166 'recordsFilteredInactive': users_data_total_filtered_inactive_count
167 })
167 })
168
168
169 return data
169 return data
170
170
171 def _set_personal_repo_group_template_vars(self, c_obj):
171 def _set_personal_repo_group_template_vars(self, c_obj):
172 DummyUser = AttributeDict({
172 DummyUser = AttributeDict({
173 'username': '${username}',
173 'username': '${username}',
174 'user_id': '${user_id}',
174 'user_id': '${user_id}',
175 })
175 })
176 c_obj.default_create_repo_group = RepoGroupModel() \
176 c_obj.default_create_repo_group = RepoGroupModel() \
177 .get_default_create_personal_repo_group()
177 .get_default_create_personal_repo_group()
178 c_obj.personal_repo_group_name = RepoGroupModel() \
178 c_obj.personal_repo_group_name = RepoGroupModel() \
179 .get_personal_group_name(DummyUser)
179 .get_personal_group_name(DummyUser)
180
180
181 @LoginRequired()
181 @LoginRequired()
182 @HasPermissionAllDecorator('hg.admin')
182 @HasPermissionAllDecorator('hg.admin')
183 @view_config(
183 @view_config(
184 route_name='users_new', request_method='GET',
184 route_name='users_new', request_method='GET',
185 renderer='rhodecode:templates/admin/users/user_add.mako')
185 renderer='rhodecode:templates/admin/users/user_add.mako')
186 def users_new(self):
186 def users_new(self):
187 _ = self.request.translate
187 _ = self.request.translate
188 c = self.load_default_context()
188 c = self.load_default_context()
189 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
189 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
190 self._set_personal_repo_group_template_vars(c)
190 self._set_personal_repo_group_template_vars(c)
191 return self._get_template_context(c)
191 return self._get_template_context(c)
192
192
193 @LoginRequired()
193 @LoginRequired()
194 @HasPermissionAllDecorator('hg.admin')
194 @HasPermissionAllDecorator('hg.admin')
195 @CSRFRequired()
195 @CSRFRequired()
196 @view_config(
196 @view_config(
197 route_name='users_create', request_method='POST',
197 route_name='users_create', request_method='POST',
198 renderer='rhodecode:templates/admin/users/user_add.mako')
198 renderer='rhodecode:templates/admin/users/user_add.mako')
199 def users_create(self):
199 def users_create(self):
200 _ = self.request.translate
200 _ = self.request.translate
201 c = self.load_default_context()
201 c = self.load_default_context()
202 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
202 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
203 user_model = UserModel()
203 user_model = UserModel()
204 user_form = UserForm(self.request.translate)()
204 user_form = UserForm(self.request.translate)()
205 try:
205 try:
206 form_result = user_form.to_python(dict(self.request.POST))
206 form_result = user_form.to_python(dict(self.request.POST))
207 user = user_model.create(form_result)
207 user = user_model.create(form_result)
208 Session().flush()
208 Session().flush()
209 creation_data = user.get_api_data()
209 creation_data = user.get_api_data()
210 username = form_result['username']
210 username = form_result['username']
211
211
212 audit_logger.store_web(
212 audit_logger.store_web(
213 'user.create', action_data={'data': creation_data},
213 'user.create', action_data={'data': creation_data},
214 user=c.rhodecode_user)
214 user=c.rhodecode_user)
215
215
216 user_link = h.link_to(
216 user_link = h.link_to(
217 h.escape(username),
217 h.escape(username),
218 h.route_path('user_edit', user_id=user.user_id))
218 h.route_path('user_edit', user_id=user.user_id))
219 h.flash(h.literal(_('Created user %(user_link)s')
219 h.flash(h.literal(_('Created user %(user_link)s')
220 % {'user_link': user_link}), category='success')
220 % {'user_link': user_link}), category='success')
221 Session().commit()
221 Session().commit()
222 except formencode.Invalid as errors:
222 except formencode.Invalid as errors:
223 self._set_personal_repo_group_template_vars(c)
223 self._set_personal_repo_group_template_vars(c)
224 data = render(
224 data = render(
225 'rhodecode:templates/admin/users/user_add.mako',
225 'rhodecode:templates/admin/users/user_add.mako',
226 self._get_template_context(c), self.request)
226 self._get_template_context(c), self.request)
227 html = formencode.htmlfill.render(
227 html = formencode.htmlfill.render(
228 data,
228 data,
229 defaults=errors.value,
229 defaults=errors.value,
230 errors=errors.error_dict or {},
230 errors=errors.error_dict or {},
231 prefix_error=False,
231 prefix_error=False,
232 encoding="UTF-8",
232 encoding="UTF-8",
233 force_defaults=False
233 force_defaults=False
234 )
234 )
235 return Response(html)
235 return Response(html)
236 except UserCreationError as e:
236 except UserCreationError as e:
237 h.flash(e, 'error')
237 h.flash(e, 'error')
238 except Exception:
238 except Exception:
239 log.exception("Exception creation of user")
239 log.exception("Exception creation of user")
240 h.flash(_('Error occurred during creation of user %s')
240 h.flash(_('Error occurred during creation of user %s')
241 % self.request.POST.get('username'), category='error')
241 % self.request.POST.get('username'), category='error')
242 raise HTTPFound(h.route_path('users'))
242 raise HTTPFound(h.route_path('users'))
243
243
244
244
245 class UsersView(UserAppView):
245 class UsersView(UserAppView):
246 ALLOW_SCOPED_TOKENS = False
246 ALLOW_SCOPED_TOKENS = False
247 """
247 """
248 This view has alternative version inside EE, if modified please take a look
248 This view has alternative version inside EE, if modified please take a look
249 in there as well.
249 in there as well.
250 """
250 """
251
251
252 def load_default_context(self):
252 def load_default_context(self):
253 c = self._get_local_tmpl_context()
253 c = self._get_local_tmpl_context()
254 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
254 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
255 c.allowed_languages = [
255 c.allowed_languages = [
256 ('en', 'English (en)'),
256 ('en', 'English (en)'),
257 ('de', 'German (de)'),
257 ('de', 'German (de)'),
258 ('fr', 'French (fr)'),
258 ('fr', 'French (fr)'),
259 ('it', 'Italian (it)'),
259 ('it', 'Italian (it)'),
260 ('ja', 'Japanese (ja)'),
260 ('ja', 'Japanese (ja)'),
261 ('pl', 'Polish (pl)'),
261 ('pl', 'Polish (pl)'),
262 ('pt', 'Portuguese (pt)'),
262 ('pt', 'Portuguese (pt)'),
263 ('ru', 'Russian (ru)'),
263 ('ru', 'Russian (ru)'),
264 ('zh', 'Chinese (zh)'),
264 ('zh', 'Chinese (zh)'),
265 ]
265 ]
266 req = self.request
266 req = self.request
267
267
268 c.available_permissions = req.registry.settings['available_permissions']
268 c.available_permissions = req.registry.settings['available_permissions']
269 PermissionModel().set_global_permission_choices(
269 PermissionModel().set_global_permission_choices(
270 c, gettext_translator=req.translate)
270 c, gettext_translator=req.translate)
271
271
272 return c
272 return c
273
273
274 @LoginRequired()
274 @LoginRequired()
275 @HasPermissionAllDecorator('hg.admin')
275 @HasPermissionAllDecorator('hg.admin')
276 @CSRFRequired()
276 @CSRFRequired()
277 @view_config(
277 @view_config(
278 route_name='user_update', request_method='POST',
278 route_name='user_update', request_method='POST',
279 renderer='rhodecode:templates/admin/users/user_edit.mako')
279 renderer='rhodecode:templates/admin/users/user_edit.mako')
280 def user_update(self):
280 def user_update(self):
281 _ = self.request.translate
281 _ = self.request.translate
282 c = self.load_default_context()
282 c = self.load_default_context()
283
283
284 user_id = self.db_user_id
284 user_id = self.db_user_id
285 c.user = self.db_user
285 c.user = self.db_user
286
286
287 c.active = 'profile'
287 c.active = 'profile'
288 c.extern_type = c.user.extern_type
288 c.extern_type = c.user.extern_type
289 c.extern_name = c.user.extern_name
289 c.extern_name = c.user.extern_name
290 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
290 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
291 available_languages = [x[0] for x in c.allowed_languages]
291 available_languages = [x[0] for x in c.allowed_languages]
292 _form = UserForm(self.request.translate, edit=True,
292 _form = UserForm(self.request.translate, edit=True,
293 available_languages=available_languages,
293 available_languages=available_languages,
294 old_data={'user_id': user_id,
294 old_data={'user_id': user_id,
295 'email': c.user.email})()
295 'email': c.user.email})()
296 form_result = {}
296 form_result = {}
297 old_values = c.user.get_api_data()
297 old_values = c.user.get_api_data()
298 try:
298 try:
299 form_result = _form.to_python(dict(self.request.POST))
299 form_result = _form.to_python(dict(self.request.POST))
300 skip_attrs = ['extern_type', 'extern_name']
300 skip_attrs = ['extern_type', 'extern_name']
301 # TODO: plugin should define if username can be updated
301 # TODO: plugin should define if username can be updated
302 if c.extern_type != "rhodecode":
302 if c.extern_type != "rhodecode":
303 # forbid updating username for external accounts
303 # forbid updating username for external accounts
304 skip_attrs.append('username')
304 skip_attrs.append('username')
305
305
306 UserModel().update_user(
306 UserModel().update_user(
307 user_id, skip_attrs=skip_attrs, **form_result)
307 user_id, skip_attrs=skip_attrs, **form_result)
308
308
309 audit_logger.store_web(
309 audit_logger.store_web(
310 'user.edit', action_data={'old_data': old_values},
310 'user.edit', action_data={'old_data': old_values},
311 user=c.rhodecode_user)
311 user=c.rhodecode_user)
312
312
313 Session().commit()
313 Session().commit()
314 h.flash(_('User updated successfully'), category='success')
314 h.flash(_('User updated successfully'), category='success')
315 except formencode.Invalid as errors:
315 except formencode.Invalid as errors:
316 data = render(
316 data = render(
317 'rhodecode:templates/admin/users/user_edit.mako',
317 'rhodecode:templates/admin/users/user_edit.mako',
318 self._get_template_context(c), self.request)
318 self._get_template_context(c), self.request)
319 html = formencode.htmlfill.render(
319 html = formencode.htmlfill.render(
320 data,
320 data,
321 defaults=errors.value,
321 defaults=errors.value,
322 errors=errors.error_dict or {},
322 errors=errors.error_dict or {},
323 prefix_error=False,
323 prefix_error=False,
324 encoding="UTF-8",
324 encoding="UTF-8",
325 force_defaults=False
325 force_defaults=False
326 )
326 )
327 return Response(html)
327 return Response(html)
328 except UserCreationError as e:
328 except UserCreationError as e:
329 h.flash(e, 'error')
329 h.flash(e, 'error')
330 except Exception:
330 except Exception:
331 log.exception("Exception updating user")
331 log.exception("Exception updating user")
332 h.flash(_('Error occurred during update of user %s')
332 h.flash(_('Error occurred during update of user %s')
333 % form_result.get('username'), category='error')
333 % form_result.get('username'), category='error')
334 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
334 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
335
335
336 @LoginRequired()
336 @LoginRequired()
337 @HasPermissionAllDecorator('hg.admin')
337 @HasPermissionAllDecorator('hg.admin')
338 @CSRFRequired()
338 @CSRFRequired()
339 @view_config(
339 @view_config(
340 route_name='user_delete', request_method='POST',
340 route_name='user_delete', request_method='POST',
341 renderer='rhodecode:templates/admin/users/user_edit.mako')
341 renderer='rhodecode:templates/admin/users/user_edit.mako')
342 def user_delete(self):
342 def user_delete(self):
343 _ = self.request.translate
343 _ = self.request.translate
344 c = self.load_default_context()
344 c = self.load_default_context()
345 c.user = self.db_user
345 c.user = self.db_user
346
346
347 _repos = c.user.repositories
347 _repos = c.user.repositories
348 _repo_groups = c.user.repository_groups
348 _repo_groups = c.user.repository_groups
349 _user_groups = c.user.user_groups
349 _user_groups = c.user.user_groups
350
350
351 handle_repos = None
351 handle_repos = None
352 handle_repo_groups = None
352 handle_repo_groups = None
353 handle_user_groups = None
353 handle_user_groups = None
354 # dummy call for flash of handle
354 # dummy call for flash of handle
355 set_handle_flash_repos = lambda: None
355 set_handle_flash_repos = lambda: None
356 set_handle_flash_repo_groups = lambda: None
356 set_handle_flash_repo_groups = lambda: None
357 set_handle_flash_user_groups = lambda: None
357 set_handle_flash_user_groups = lambda: None
358
358
359 if _repos and self.request.POST.get('user_repos'):
359 if _repos and self.request.POST.get('user_repos'):
360 do = self.request.POST['user_repos']
360 do = self.request.POST['user_repos']
361 if do == 'detach':
361 if do == 'detach':
362 handle_repos = 'detach'
362 handle_repos = 'detach'
363 set_handle_flash_repos = lambda: h.flash(
363 set_handle_flash_repos = lambda: h.flash(
364 _('Detached %s repositories') % len(_repos),
364 _('Detached %s repositories') % len(_repos),
365 category='success')
365 category='success')
366 elif do == 'delete':
366 elif do == 'delete':
367 handle_repos = 'delete'
367 handle_repos = 'delete'
368 set_handle_flash_repos = lambda: h.flash(
368 set_handle_flash_repos = lambda: h.flash(
369 _('Deleted %s repositories') % len(_repos),
369 _('Deleted %s repositories') % len(_repos),
370 category='success')
370 category='success')
371
371
372 if _repo_groups and self.request.POST.get('user_repo_groups'):
372 if _repo_groups and self.request.POST.get('user_repo_groups'):
373 do = self.request.POST['user_repo_groups']
373 do = self.request.POST['user_repo_groups']
374 if do == 'detach':
374 if do == 'detach':
375 handle_repo_groups = 'detach'
375 handle_repo_groups = 'detach'
376 set_handle_flash_repo_groups = lambda: h.flash(
376 set_handle_flash_repo_groups = lambda: h.flash(
377 _('Detached %s repository groups') % len(_repo_groups),
377 _('Detached %s repository groups') % len(_repo_groups),
378 category='success')
378 category='success')
379 elif do == 'delete':
379 elif do == 'delete':
380 handle_repo_groups = 'delete'
380 handle_repo_groups = 'delete'
381 set_handle_flash_repo_groups = lambda: h.flash(
381 set_handle_flash_repo_groups = lambda: h.flash(
382 _('Deleted %s repository groups') % len(_repo_groups),
382 _('Deleted %s repository groups') % len(_repo_groups),
383 category='success')
383 category='success')
384
384
385 if _user_groups and self.request.POST.get('user_user_groups'):
385 if _user_groups and self.request.POST.get('user_user_groups'):
386 do = self.request.POST['user_user_groups']
386 do = self.request.POST['user_user_groups']
387 if do == 'detach':
387 if do == 'detach':
388 handle_user_groups = 'detach'
388 handle_user_groups = 'detach'
389 set_handle_flash_user_groups = lambda: h.flash(
389 set_handle_flash_user_groups = lambda: h.flash(
390 _('Detached %s user groups') % len(_user_groups),
390 _('Detached %s user groups') % len(_user_groups),
391 category='success')
391 category='success')
392 elif do == 'delete':
392 elif do == 'delete':
393 handle_user_groups = 'delete'
393 handle_user_groups = 'delete'
394 set_handle_flash_user_groups = lambda: h.flash(
394 set_handle_flash_user_groups = lambda: h.flash(
395 _('Deleted %s user groups') % len(_user_groups),
395 _('Deleted %s user groups') % len(_user_groups),
396 category='success')
396 category='success')
397
397
398 old_values = c.user.get_api_data()
398 old_values = c.user.get_api_data()
399 try:
399 try:
400 UserModel().delete(c.user, handle_repos=handle_repos,
400 UserModel().delete(c.user, handle_repos=handle_repos,
401 handle_repo_groups=handle_repo_groups,
401 handle_repo_groups=handle_repo_groups,
402 handle_user_groups=handle_user_groups)
402 handle_user_groups=handle_user_groups)
403
403
404 audit_logger.store_web(
404 audit_logger.store_web(
405 'user.delete', action_data={'old_data': old_values},
405 'user.delete', action_data={'old_data': old_values},
406 user=c.rhodecode_user)
406 user=c.rhodecode_user)
407
407
408 Session().commit()
408 Session().commit()
409 set_handle_flash_repos()
409 set_handle_flash_repos()
410 set_handle_flash_repo_groups()
410 set_handle_flash_repo_groups()
411 set_handle_flash_user_groups()
411 set_handle_flash_user_groups()
412 h.flash(_('Successfully deleted user'), category='success')
412 h.flash(_('Successfully deleted user'), category='success')
413 except (UserOwnsReposException, UserOwnsRepoGroupsException,
413 except (UserOwnsReposException, UserOwnsRepoGroupsException,
414 UserOwnsUserGroupsException, DefaultUserException) as e:
414 UserOwnsUserGroupsException, DefaultUserException) as e:
415 h.flash(e, category='warning')
415 h.flash(e, category='warning')
416 except Exception:
416 except Exception:
417 log.exception("Exception during deletion of user")
417 log.exception("Exception during deletion of user")
418 h.flash(_('An error occurred during deletion of user'),
418 h.flash(_('An error occurred during deletion of user'),
419 category='error')
419 category='error')
420 raise HTTPFound(h.route_path('users'))
420 raise HTTPFound(h.route_path('users'))
421
421
422 @LoginRequired()
422 @LoginRequired()
423 @HasPermissionAllDecorator('hg.admin')
423 @HasPermissionAllDecorator('hg.admin')
424 @view_config(
424 @view_config(
425 route_name='user_edit', request_method='GET',
425 route_name='user_edit', request_method='GET',
426 renderer='rhodecode:templates/admin/users/user_edit.mako')
426 renderer='rhodecode:templates/admin/users/user_edit.mako')
427 def user_edit(self):
427 def user_edit(self):
428 _ = self.request.translate
428 _ = self.request.translate
429 c = self.load_default_context()
429 c = self.load_default_context()
430 c.user = self.db_user
430 c.user = self.db_user
431
431
432 c.active = 'profile'
432 c.active = 'profile'
433 c.extern_type = c.user.extern_type
433 c.extern_type = c.user.extern_type
434 c.extern_name = c.user.extern_name
434 c.extern_name = c.user.extern_name
435 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
435 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
436
436
437 defaults = c.user.get_dict()
437 defaults = c.user.get_dict()
438 defaults.update({'language': c.user.user_data.get('language')})
438 defaults.update({'language': c.user.user_data.get('language')})
439
439
440 data = render(
440 data = render(
441 'rhodecode:templates/admin/users/user_edit.mako',
441 'rhodecode:templates/admin/users/user_edit.mako',
442 self._get_template_context(c), self.request)
442 self._get_template_context(c), self.request)
443 html = formencode.htmlfill.render(
443 html = formencode.htmlfill.render(
444 data,
444 data,
445 defaults=defaults,
445 defaults=defaults,
446 encoding="UTF-8",
446 encoding="UTF-8",
447 force_defaults=False
447 force_defaults=False
448 )
448 )
449 return Response(html)
449 return Response(html)
450
450
451 @LoginRequired()
451 @LoginRequired()
452 @HasPermissionAllDecorator('hg.admin')
452 @HasPermissionAllDecorator('hg.admin')
453 @view_config(
453 @view_config(
454 route_name='user_edit_advanced', request_method='GET',
454 route_name='user_edit_advanced', request_method='GET',
455 renderer='rhodecode:templates/admin/users/user_edit.mako')
455 renderer='rhodecode:templates/admin/users/user_edit.mako')
456 def user_edit_advanced(self):
456 def user_edit_advanced(self):
457 _ = self.request.translate
457 _ = self.request.translate
458 c = self.load_default_context()
458 c = self.load_default_context()
459
459
460 user_id = self.db_user_id
460 user_id = self.db_user_id
461 c.user = self.db_user
461 c.user = self.db_user
462
462
463 c.active = 'advanced'
463 c.active = 'advanced'
464 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
464 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
465 c.personal_repo_group_name = RepoGroupModel()\
465 c.personal_repo_group_name = RepoGroupModel()\
466 .get_personal_group_name(c.user)
466 .get_personal_group_name(c.user)
467
467
468 c.user_to_review_rules = sorted(
468 c.user_to_review_rules = sorted(
469 (x.user for x in c.user.user_review_rules),
469 (x.user for x in c.user.user_review_rules),
470 key=lambda u: u.username.lower())
470 key=lambda u: u.username.lower())
471
471
472 c.first_admin = User.get_first_super_admin()
472 c.first_admin = User.get_first_super_admin()
473 defaults = c.user.get_dict()
473 defaults = c.user.get_dict()
474
474
475 # Interim workaround if the user participated on any pull requests as a
475 # Interim workaround if the user participated on any pull requests as a
476 # reviewer.
476 # reviewer.
477 has_review = len(c.user.reviewer_pull_requests)
477 has_review = len(c.user.reviewer_pull_requests)
478 c.can_delete_user = not has_review
478 c.can_delete_user = not has_review
479 c.can_delete_user_message = ''
479 c.can_delete_user_message = ''
480 inactive_link = h.link_to(
480 inactive_link = h.link_to(
481 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
481 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
482 if has_review == 1:
482 if has_review == 1:
483 c.can_delete_user_message = h.literal(_(
483 c.can_delete_user_message = h.literal(_(
484 'The user participates as reviewer in {} pull request and '
484 'The user participates as reviewer in {} pull request and '
485 'cannot be deleted. \nYou can set the user to '
485 'cannot be deleted. \nYou can set the user to '
486 '"{}" instead of deleting it.').format(
486 '"{}" instead of deleting it.').format(
487 has_review, inactive_link))
487 has_review, inactive_link))
488 elif has_review:
488 elif has_review:
489 c.can_delete_user_message = h.literal(_(
489 c.can_delete_user_message = h.literal(_(
490 'The user participates as reviewer in {} pull requests and '
490 'The user participates as reviewer in {} pull requests and '
491 'cannot be deleted. \nYou can set the user to '
491 'cannot be deleted. \nYou can set the user to '
492 '"{}" instead of deleting it.').format(
492 '"{}" instead of deleting it.').format(
493 has_review, inactive_link))
493 has_review, inactive_link))
494
494
495 data = render(
495 data = render(
496 'rhodecode:templates/admin/users/user_edit.mako',
496 'rhodecode:templates/admin/users/user_edit.mako',
497 self._get_template_context(c), self.request)
497 self._get_template_context(c), self.request)
498 html = formencode.htmlfill.render(
498 html = formencode.htmlfill.render(
499 data,
499 data,
500 defaults=defaults,
500 defaults=defaults,
501 encoding="UTF-8",
501 encoding="UTF-8",
502 force_defaults=False
502 force_defaults=False
503 )
503 )
504 return Response(html)
504 return Response(html)
505
505
506 @LoginRequired()
506 @LoginRequired()
507 @HasPermissionAllDecorator('hg.admin')
507 @HasPermissionAllDecorator('hg.admin')
508 @view_config(
508 @view_config(
509 route_name='user_edit_global_perms', request_method='GET',
509 route_name='user_edit_global_perms', request_method='GET',
510 renderer='rhodecode:templates/admin/users/user_edit.mako')
510 renderer='rhodecode:templates/admin/users/user_edit.mako')
511 def user_edit_global_perms(self):
511 def user_edit_global_perms(self):
512 _ = self.request.translate
512 _ = self.request.translate
513 c = self.load_default_context()
513 c = self.load_default_context()
514 c.user = self.db_user
514 c.user = self.db_user
515
515
516 c.active = 'global_perms'
516 c.active = 'global_perms'
517
517
518 c.default_user = User.get_default_user()
518 c.default_user = User.get_default_user()
519 defaults = c.user.get_dict()
519 defaults = c.user.get_dict()
520 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
520 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
521 defaults.update(c.default_user.get_default_perms())
521 defaults.update(c.default_user.get_default_perms())
522 defaults.update(c.user.get_default_perms())
522 defaults.update(c.user.get_default_perms())
523
523
524 data = render(
524 data = render(
525 'rhodecode:templates/admin/users/user_edit.mako',
525 'rhodecode:templates/admin/users/user_edit.mako',
526 self._get_template_context(c), self.request)
526 self._get_template_context(c), self.request)
527 html = formencode.htmlfill.render(
527 html = formencode.htmlfill.render(
528 data,
528 data,
529 defaults=defaults,
529 defaults=defaults,
530 encoding="UTF-8",
530 encoding="UTF-8",
531 force_defaults=False
531 force_defaults=False
532 )
532 )
533 return Response(html)
533 return Response(html)
534
534
535 @LoginRequired()
535 @LoginRequired()
536 @HasPermissionAllDecorator('hg.admin')
536 @HasPermissionAllDecorator('hg.admin')
537 @CSRFRequired()
537 @CSRFRequired()
538 @view_config(
538 @view_config(
539 route_name='user_edit_global_perms_update', request_method='POST',
539 route_name='user_edit_global_perms_update', request_method='POST',
540 renderer='rhodecode:templates/admin/users/user_edit.mako')
540 renderer='rhodecode:templates/admin/users/user_edit.mako')
541 def user_edit_global_perms_update(self):
541 def user_edit_global_perms_update(self):
542 _ = self.request.translate
542 _ = self.request.translate
543 c = self.load_default_context()
543 c = self.load_default_context()
544
544
545 user_id = self.db_user_id
545 user_id = self.db_user_id
546 c.user = self.db_user
546 c.user = self.db_user
547
547
548 c.active = 'global_perms'
548 c.active = 'global_perms'
549 try:
549 try:
550 # first stage that verifies the checkbox
550 # first stage that verifies the checkbox
551 _form = UserIndividualPermissionsForm(self.request.translate)
551 _form = UserIndividualPermissionsForm(self.request.translate)
552 form_result = _form.to_python(dict(self.request.POST))
552 form_result = _form.to_python(dict(self.request.POST))
553 inherit_perms = form_result['inherit_default_permissions']
553 inherit_perms = form_result['inherit_default_permissions']
554 c.user.inherit_default_permissions = inherit_perms
554 c.user.inherit_default_permissions = inherit_perms
555 Session().add(c.user)
555 Session().add(c.user)
556
556
557 if not inherit_perms:
557 if not inherit_perms:
558 # only update the individual ones if we un check the flag
558 # only update the individual ones if we un check the flag
559 _form = UserPermissionsForm(
559 _form = UserPermissionsForm(
560 self.request.translate,
560 self.request.translate,
561 [x[0] for x in c.repo_create_choices],
561 [x[0] for x in c.repo_create_choices],
562 [x[0] for x in c.repo_create_on_write_choices],
562 [x[0] for x in c.repo_create_on_write_choices],
563 [x[0] for x in c.repo_group_create_choices],
563 [x[0] for x in c.repo_group_create_choices],
564 [x[0] for x in c.user_group_create_choices],
564 [x[0] for x in c.user_group_create_choices],
565 [x[0] for x in c.fork_choices],
565 [x[0] for x in c.fork_choices],
566 [x[0] for x in c.inherit_default_permission_choices])()
566 [x[0] for x in c.inherit_default_permission_choices])()
567
567
568 form_result = _form.to_python(dict(self.request.POST))
568 form_result = _form.to_python(dict(self.request.POST))
569 form_result.update({'perm_user_id': c.user.user_id})
569 form_result.update({'perm_user_id': c.user.user_id})
570
570
571 PermissionModel().update_user_permissions(form_result)
571 PermissionModel().update_user_permissions(form_result)
572
572
573 # TODO(marcink): implement global permissions
573 # TODO(marcink): implement global permissions
574 # audit_log.store_web('user.edit.permissions')
574 # audit_log.store_web('user.edit.permissions')
575
575
576 Session().commit()
576 Session().commit()
577
577
578 h.flash(_('User global permissions updated successfully'),
578 h.flash(_('User global permissions updated successfully'),
579 category='success')
579 category='success')
580
580
581 except formencode.Invalid as errors:
581 except formencode.Invalid as errors:
582 data = render(
582 data = render(
583 'rhodecode:templates/admin/users/user_edit.mako',
583 'rhodecode:templates/admin/users/user_edit.mako',
584 self._get_template_context(c), self.request)
584 self._get_template_context(c), self.request)
585 html = formencode.htmlfill.render(
585 html = formencode.htmlfill.render(
586 data,
586 data,
587 defaults=errors.value,
587 defaults=errors.value,
588 errors=errors.error_dict or {},
588 errors=errors.error_dict or {},
589 prefix_error=False,
589 prefix_error=False,
590 encoding="UTF-8",
590 encoding="UTF-8",
591 force_defaults=False
591 force_defaults=False
592 )
592 )
593 return Response(html)
593 return Response(html)
594 except Exception:
594 except Exception:
595 log.exception("Exception during permissions saving")
595 log.exception("Exception during permissions saving")
596 h.flash(_('An error occurred during permissions saving'),
596 h.flash(_('An error occurred during permissions saving'),
597 category='error')
597 category='error')
598
598
599 affected_user_ids = [user_id]
599 affected_user_ids = [user_id]
600 events.trigger(events.UserPermissionsChange(affected_user_ids))
600 events.trigger(events.UserPermissionsChange(affected_user_ids))
601 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
601 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
602
602
603 @LoginRequired()
603 @LoginRequired()
604 @HasPermissionAllDecorator('hg.admin')
604 @HasPermissionAllDecorator('hg.admin')
605 @CSRFRequired()
605 @CSRFRequired()
606 @view_config(
606 @view_config(
607 route_name='user_enable_force_password_reset', request_method='POST',
607 route_name='user_enable_force_password_reset', request_method='POST',
608 renderer='rhodecode:templates/admin/users/user_edit.mako')
608 renderer='rhodecode:templates/admin/users/user_edit.mako')
609 def user_enable_force_password_reset(self):
609 def user_enable_force_password_reset(self):
610 _ = self.request.translate
610 _ = self.request.translate
611 c = self.load_default_context()
611 c = self.load_default_context()
612
612
613 user_id = self.db_user_id
613 user_id = self.db_user_id
614 c.user = self.db_user
614 c.user = self.db_user
615
615
616 try:
616 try:
617 c.user.update_userdata(force_password_change=True)
617 c.user.update_userdata(force_password_change=True)
618
618
619 msg = _('Force password change enabled for user')
619 msg = _('Force password change enabled for user')
620 audit_logger.store_web('user.edit.password_reset.enabled',
620 audit_logger.store_web('user.edit.password_reset.enabled',
621 user=c.rhodecode_user)
621 user=c.rhodecode_user)
622
622
623 Session().commit()
623 Session().commit()
624 h.flash(msg, category='success')
624 h.flash(msg, category='success')
625 except Exception:
625 except Exception:
626 log.exception("Exception during password reset for user")
626 log.exception("Exception during password reset for user")
627 h.flash(_('An error occurred during password reset for user'),
627 h.flash(_('An error occurred during password reset for user'),
628 category='error')
628 category='error')
629
629
630 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
630 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
631
631
632 @LoginRequired()
632 @LoginRequired()
633 @HasPermissionAllDecorator('hg.admin')
633 @HasPermissionAllDecorator('hg.admin')
634 @CSRFRequired()
634 @CSRFRequired()
635 @view_config(
635 @view_config(
636 route_name='user_disable_force_password_reset', request_method='POST',
636 route_name='user_disable_force_password_reset', request_method='POST',
637 renderer='rhodecode:templates/admin/users/user_edit.mako')
637 renderer='rhodecode:templates/admin/users/user_edit.mako')
638 def user_disable_force_password_reset(self):
638 def user_disable_force_password_reset(self):
639 _ = self.request.translate
639 _ = self.request.translate
640 c = self.load_default_context()
640 c = self.load_default_context()
641
641
642 user_id = self.db_user_id
642 user_id = self.db_user_id
643 c.user = self.db_user
643 c.user = self.db_user
644
644
645 try:
645 try:
646 c.user.update_userdata(force_password_change=False)
646 c.user.update_userdata(force_password_change=False)
647
647
648 msg = _('Force password change disabled for user')
648 msg = _('Force password change disabled for user')
649 audit_logger.store_web(
649 audit_logger.store_web(
650 'user.edit.password_reset.disabled',
650 'user.edit.password_reset.disabled',
651 user=c.rhodecode_user)
651 user=c.rhodecode_user)
652
652
653 Session().commit()
653 Session().commit()
654 h.flash(msg, category='success')
654 h.flash(msg, category='success')
655 except Exception:
655 except Exception:
656 log.exception("Exception during password reset for user")
656 log.exception("Exception during password reset for user")
657 h.flash(_('An error occurred during password reset for user'),
657 h.flash(_('An error occurred during password reset for user'),
658 category='error')
658 category='error')
659
659
660 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
660 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
661
661
662 @LoginRequired()
662 @LoginRequired()
663 @HasPermissionAllDecorator('hg.admin')
663 @HasPermissionAllDecorator('hg.admin')
664 @CSRFRequired()
664 @CSRFRequired()
665 @view_config(
665 @view_config(
666 route_name='user_create_personal_repo_group', request_method='POST',
666 route_name='user_create_personal_repo_group', request_method='POST',
667 renderer='rhodecode:templates/admin/users/user_edit.mako')
667 renderer='rhodecode:templates/admin/users/user_edit.mako')
668 def user_create_personal_repo_group(self):
668 def user_create_personal_repo_group(self):
669 """
669 """
670 Create personal repository group for this user
670 Create personal repository group for this user
671 """
671 """
672 from rhodecode.model.repo_group import RepoGroupModel
672 from rhodecode.model.repo_group import RepoGroupModel
673
673
674 _ = self.request.translate
674 _ = self.request.translate
675 c = self.load_default_context()
675 c = self.load_default_context()
676
676
677 user_id = self.db_user_id
677 user_id = self.db_user_id
678 c.user = self.db_user
678 c.user = self.db_user
679
679
680 personal_repo_group = RepoGroup.get_user_personal_repo_group(
680 personal_repo_group = RepoGroup.get_user_personal_repo_group(
681 c.user.user_id)
681 c.user.user_id)
682 if personal_repo_group:
682 if personal_repo_group:
683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
684
684
685 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
685 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
686 c.user)
687 named_personal_group = RepoGroup.get_by_group_name(
686 named_personal_group = RepoGroup.get_by_group_name(
688 personal_repo_group_name)
687 personal_repo_group_name)
689 try:
688 try:
690
689
691 if named_personal_group and named_personal_group.user_id == c.user.user_id:
690 if named_personal_group and named_personal_group.user_id == c.user.user_id:
692 # migrate the same named group, and mark it as personal
691 # migrate the same named group, and mark it as personal
693 named_personal_group.personal = True
692 named_personal_group.personal = True
694 Session().add(named_personal_group)
693 Session().add(named_personal_group)
695 Session().commit()
694 Session().commit()
696 msg = _('Linked repository group `%s` as personal' % (
695 msg = _('Linked repository group `%s` as personal' % (
697 personal_repo_group_name,))
696 personal_repo_group_name,))
698 h.flash(msg, category='success')
697 h.flash(msg, category='success')
699 elif not named_personal_group:
698 elif not named_personal_group:
700 RepoGroupModel().create_personal_repo_group(c.user)
699 RepoGroupModel().create_personal_repo_group(c.user)
701
700
702 msg = _('Created repository group `%s`' % (
701 msg = _('Created repository group `%s`' % (
703 personal_repo_group_name,))
702 personal_repo_group_name,))
704 h.flash(msg, category='success')
703 h.flash(msg, category='success')
705 else:
704 else:
706 msg = _('Repository group `%s` is already taken' % (
705 msg = _('Repository group `%s` is already taken' % (
707 personal_repo_group_name,))
706 personal_repo_group_name,))
708 h.flash(msg, category='warning')
707 h.flash(msg, category='warning')
709 except Exception:
708 except Exception:
710 log.exception("Exception during repository group creation")
709 log.exception("Exception during repository group creation")
711 msg = _(
710 msg = _(
712 'An error occurred during repository group creation for user')
711 'An error occurred during repository group creation for user')
713 h.flash(msg, category='error')
712 h.flash(msg, category='error')
714 Session().rollback()
713 Session().rollback()
715
714
716 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
715 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
717
716
718 @LoginRequired()
717 @LoginRequired()
719 @HasPermissionAllDecorator('hg.admin')
718 @HasPermissionAllDecorator('hg.admin')
720 @view_config(
719 @view_config(
721 route_name='edit_user_auth_tokens', request_method='GET',
720 route_name='edit_user_auth_tokens', request_method='GET',
722 renderer='rhodecode:templates/admin/users/user_edit.mako')
721 renderer='rhodecode:templates/admin/users/user_edit.mako')
723 def auth_tokens(self):
722 def auth_tokens(self):
724 _ = self.request.translate
723 _ = self.request.translate
725 c = self.load_default_context()
724 c = self.load_default_context()
726 c.user = self.db_user
725 c.user = self.db_user
727
726
728 c.active = 'auth_tokens'
727 c.active = 'auth_tokens'
729
728
730 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
729 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
731 c.role_values = [
730 c.role_values = [
732 (x, AuthTokenModel.cls._get_role_name(x))
731 (x, AuthTokenModel.cls._get_role_name(x))
733 for x in AuthTokenModel.cls.ROLES]
732 for x in AuthTokenModel.cls.ROLES]
734 c.role_options = [(c.role_values, _("Role"))]
733 c.role_options = [(c.role_values, _("Role"))]
735 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
734 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
736 c.user.user_id, show_expired=True)
735 c.user.user_id, show_expired=True)
737 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
736 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
738 return self._get_template_context(c)
737 return self._get_template_context(c)
739
738
740 def maybe_attach_token_scope(self, token):
739 def maybe_attach_token_scope(self, token):
741 # implemented in EE edition
740 # implemented in EE edition
742 pass
741 pass
743
742
744 @LoginRequired()
743 @LoginRequired()
745 @HasPermissionAllDecorator('hg.admin')
744 @HasPermissionAllDecorator('hg.admin')
746 @CSRFRequired()
745 @CSRFRequired()
747 @view_config(
746 @view_config(
748 route_name='edit_user_auth_tokens_add', request_method='POST')
747 route_name='edit_user_auth_tokens_add', request_method='POST')
749 def auth_tokens_add(self):
748 def auth_tokens_add(self):
750 _ = self.request.translate
749 _ = self.request.translate
751 c = self.load_default_context()
750 c = self.load_default_context()
752
751
753 user_id = self.db_user_id
752 user_id = self.db_user_id
754 c.user = self.db_user
753 c.user = self.db_user
755
754
756 user_data = c.user.get_api_data()
755 user_data = c.user.get_api_data()
757 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
756 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
758 description = self.request.POST.get('description')
757 description = self.request.POST.get('description')
759 role = self.request.POST.get('role')
758 role = self.request.POST.get('role')
760
759
761 token = UserModel().add_auth_token(
760 token = UserModel().add_auth_token(
762 user=c.user.user_id,
761 user=c.user.user_id,
763 lifetime_minutes=lifetime, role=role, description=description,
762 lifetime_minutes=lifetime, role=role, description=description,
764 scope_callback=self.maybe_attach_token_scope)
763 scope_callback=self.maybe_attach_token_scope)
765 token_data = token.get_api_data()
764 token_data = token.get_api_data()
766
765
767 audit_logger.store_web(
766 audit_logger.store_web(
768 'user.edit.token.add', action_data={
767 'user.edit.token.add', action_data={
769 'data': {'token': token_data, 'user': user_data}},
768 'data': {'token': token_data, 'user': user_data}},
770 user=self._rhodecode_user, )
769 user=self._rhodecode_user, )
771 Session().commit()
770 Session().commit()
772
771
773 h.flash(_("Auth token successfully created"), category='success')
772 h.flash(_("Auth token successfully created"), category='success')
774 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
773 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
775
774
776 @LoginRequired()
775 @LoginRequired()
777 @HasPermissionAllDecorator('hg.admin')
776 @HasPermissionAllDecorator('hg.admin')
778 @CSRFRequired()
777 @CSRFRequired()
779 @view_config(
778 @view_config(
780 route_name='edit_user_auth_tokens_delete', request_method='POST')
779 route_name='edit_user_auth_tokens_delete', request_method='POST')
781 def auth_tokens_delete(self):
780 def auth_tokens_delete(self):
782 _ = self.request.translate
781 _ = self.request.translate
783 c = self.load_default_context()
782 c = self.load_default_context()
784
783
785 user_id = self.db_user_id
784 user_id = self.db_user_id
786 c.user = self.db_user
785 c.user = self.db_user
787
786
788 user_data = c.user.get_api_data()
787 user_data = c.user.get_api_data()
789
788
790 del_auth_token = self.request.POST.get('del_auth_token')
789 del_auth_token = self.request.POST.get('del_auth_token')
791
790
792 if del_auth_token:
791 if del_auth_token:
793 token = UserApiKeys.get_or_404(del_auth_token)
792 token = UserApiKeys.get_or_404(del_auth_token)
794 token_data = token.get_api_data()
793 token_data = token.get_api_data()
795
794
796 AuthTokenModel().delete(del_auth_token, c.user.user_id)
795 AuthTokenModel().delete(del_auth_token, c.user.user_id)
797 audit_logger.store_web(
796 audit_logger.store_web(
798 'user.edit.token.delete', action_data={
797 'user.edit.token.delete', action_data={
799 'data': {'token': token_data, 'user': user_data}},
798 'data': {'token': token_data, 'user': user_data}},
800 user=self._rhodecode_user,)
799 user=self._rhodecode_user,)
801 Session().commit()
800 Session().commit()
802 h.flash(_("Auth token successfully deleted"), category='success')
801 h.flash(_("Auth token successfully deleted"), category='success')
803
802
804 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
803 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
805
804
806 @LoginRequired()
805 @LoginRequired()
807 @HasPermissionAllDecorator('hg.admin')
806 @HasPermissionAllDecorator('hg.admin')
808 @view_config(
807 @view_config(
809 route_name='edit_user_ssh_keys', request_method='GET',
808 route_name='edit_user_ssh_keys', request_method='GET',
810 renderer='rhodecode:templates/admin/users/user_edit.mako')
809 renderer='rhodecode:templates/admin/users/user_edit.mako')
811 def ssh_keys(self):
810 def ssh_keys(self):
812 _ = self.request.translate
811 _ = self.request.translate
813 c = self.load_default_context()
812 c = self.load_default_context()
814 c.user = self.db_user
813 c.user = self.db_user
815
814
816 c.active = 'ssh_keys'
815 c.active = 'ssh_keys'
817 c.default_key = self.request.GET.get('default_key')
816 c.default_key = self.request.GET.get('default_key')
818 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
817 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
819 return self._get_template_context(c)
818 return self._get_template_context(c)
820
819
821 @LoginRequired()
820 @LoginRequired()
822 @HasPermissionAllDecorator('hg.admin')
821 @HasPermissionAllDecorator('hg.admin')
823 @view_config(
822 @view_config(
824 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
823 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
825 renderer='rhodecode:templates/admin/users/user_edit.mako')
824 renderer='rhodecode:templates/admin/users/user_edit.mako')
826 def ssh_keys_generate_keypair(self):
825 def ssh_keys_generate_keypair(self):
827 _ = self.request.translate
826 _ = self.request.translate
828 c = self.load_default_context()
827 c = self.load_default_context()
829
828
830 c.user = self.db_user
829 c.user = self.db_user
831
830
832 c.active = 'ssh_keys_generate'
831 c.active = 'ssh_keys_generate'
833 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
832 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
834 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
833 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
835
834
836 return self._get_template_context(c)
835 return self._get_template_context(c)
837
836
838 @LoginRequired()
837 @LoginRequired()
839 @HasPermissionAllDecorator('hg.admin')
838 @HasPermissionAllDecorator('hg.admin')
840 @CSRFRequired()
839 @CSRFRequired()
841 @view_config(
840 @view_config(
842 route_name='edit_user_ssh_keys_add', request_method='POST')
841 route_name='edit_user_ssh_keys_add', request_method='POST')
843 def ssh_keys_add(self):
842 def ssh_keys_add(self):
844 _ = self.request.translate
843 _ = self.request.translate
845 c = self.load_default_context()
844 c = self.load_default_context()
846
845
847 user_id = self.db_user_id
846 user_id = self.db_user_id
848 c.user = self.db_user
847 c.user = self.db_user
849
848
850 user_data = c.user.get_api_data()
849 user_data = c.user.get_api_data()
851 key_data = self.request.POST.get('key_data')
850 key_data = self.request.POST.get('key_data')
852 description = self.request.POST.get('description')
851 description = self.request.POST.get('description')
853
852
854 fingerprint = 'unknown'
853 fingerprint = 'unknown'
855 try:
854 try:
856 if not key_data:
855 if not key_data:
857 raise ValueError('Please add a valid public key')
856 raise ValueError('Please add a valid public key')
858
857
859 key = SshKeyModel().parse_key(key_data.strip())
858 key = SshKeyModel().parse_key(key_data.strip())
860 fingerprint = key.hash_md5()
859 fingerprint = key.hash_md5()
861
860
862 ssh_key = SshKeyModel().create(
861 ssh_key = SshKeyModel().create(
863 c.user.user_id, fingerprint, key.keydata, description)
862 c.user.user_id, fingerprint, key.keydata, description)
864 ssh_key_data = ssh_key.get_api_data()
863 ssh_key_data = ssh_key.get_api_data()
865
864
866 audit_logger.store_web(
865 audit_logger.store_web(
867 'user.edit.ssh_key.add', action_data={
866 'user.edit.ssh_key.add', action_data={
868 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
867 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
869 user=self._rhodecode_user, )
868 user=self._rhodecode_user, )
870 Session().commit()
869 Session().commit()
871
870
872 # Trigger an event on change of keys.
871 # Trigger an event on change of keys.
873 trigger(SshKeyFileChangeEvent(), self.request.registry)
872 trigger(SshKeyFileChangeEvent(), self.request.registry)
874
873
875 h.flash(_("Ssh Key successfully created"), category='success')
874 h.flash(_("Ssh Key successfully created"), category='success')
876
875
877 except IntegrityError:
876 except IntegrityError:
878 log.exception("Exception during ssh key saving")
877 log.exception("Exception during ssh key saving")
879 err = 'Such key with fingerprint `{}` already exists, ' \
878 err = 'Such key with fingerprint `{}` already exists, ' \
880 'please use a different one'.format(fingerprint)
879 'please use a different one'.format(fingerprint)
881 h.flash(_('An error occurred during ssh key saving: {}').format(err),
880 h.flash(_('An error occurred during ssh key saving: {}').format(err),
882 category='error')
881 category='error')
883 except Exception as e:
882 except Exception as e:
884 log.exception("Exception during ssh key saving")
883 log.exception("Exception during ssh key saving")
885 h.flash(_('An error occurred during ssh key saving: {}').format(e),
884 h.flash(_('An error occurred during ssh key saving: {}').format(e),
886 category='error')
885 category='error')
887
886
888 return HTTPFound(
887 return HTTPFound(
889 h.route_path('edit_user_ssh_keys', user_id=user_id))
888 h.route_path('edit_user_ssh_keys', user_id=user_id))
890
889
891 @LoginRequired()
890 @LoginRequired()
892 @HasPermissionAllDecorator('hg.admin')
891 @HasPermissionAllDecorator('hg.admin')
893 @CSRFRequired()
892 @CSRFRequired()
894 @view_config(
893 @view_config(
895 route_name='edit_user_ssh_keys_delete', request_method='POST')
894 route_name='edit_user_ssh_keys_delete', request_method='POST')
896 def ssh_keys_delete(self):
895 def ssh_keys_delete(self):
897 _ = self.request.translate
896 _ = self.request.translate
898 c = self.load_default_context()
897 c = self.load_default_context()
899
898
900 user_id = self.db_user_id
899 user_id = self.db_user_id
901 c.user = self.db_user
900 c.user = self.db_user
902
901
903 user_data = c.user.get_api_data()
902 user_data = c.user.get_api_data()
904
903
905 del_ssh_key = self.request.POST.get('del_ssh_key')
904 del_ssh_key = self.request.POST.get('del_ssh_key')
906
905
907 if del_ssh_key:
906 if del_ssh_key:
908 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
907 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
909 ssh_key_data = ssh_key.get_api_data()
908 ssh_key_data = ssh_key.get_api_data()
910
909
911 SshKeyModel().delete(del_ssh_key, c.user.user_id)
910 SshKeyModel().delete(del_ssh_key, c.user.user_id)
912 audit_logger.store_web(
911 audit_logger.store_web(
913 'user.edit.ssh_key.delete', action_data={
912 'user.edit.ssh_key.delete', action_data={
914 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
913 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
915 user=self._rhodecode_user,)
914 user=self._rhodecode_user,)
916 Session().commit()
915 Session().commit()
917 # Trigger an event on change of keys.
916 # Trigger an event on change of keys.
918 trigger(SshKeyFileChangeEvent(), self.request.registry)
917 trigger(SshKeyFileChangeEvent(), self.request.registry)
919 h.flash(_("Ssh key successfully deleted"), category='success')
918 h.flash(_("Ssh key successfully deleted"), category='success')
920
919
921 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
920 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
922
921
923 @LoginRequired()
922 @LoginRequired()
924 @HasPermissionAllDecorator('hg.admin')
923 @HasPermissionAllDecorator('hg.admin')
925 @view_config(
924 @view_config(
926 route_name='edit_user_emails', request_method='GET',
925 route_name='edit_user_emails', request_method='GET',
927 renderer='rhodecode:templates/admin/users/user_edit.mako')
926 renderer='rhodecode:templates/admin/users/user_edit.mako')
928 def emails(self):
927 def emails(self):
929 _ = self.request.translate
928 _ = self.request.translate
930 c = self.load_default_context()
929 c = self.load_default_context()
931 c.user = self.db_user
930 c.user = self.db_user
932
931
933 c.active = 'emails'
932 c.active = 'emails'
934 c.user_email_map = UserEmailMap.query() \
933 c.user_email_map = UserEmailMap.query() \
935 .filter(UserEmailMap.user == c.user).all()
934 .filter(UserEmailMap.user == c.user).all()
936
935
937 return self._get_template_context(c)
936 return self._get_template_context(c)
938
937
939 @LoginRequired()
938 @LoginRequired()
940 @HasPermissionAllDecorator('hg.admin')
939 @HasPermissionAllDecorator('hg.admin')
941 @CSRFRequired()
940 @CSRFRequired()
942 @view_config(
941 @view_config(
943 route_name='edit_user_emails_add', request_method='POST')
942 route_name='edit_user_emails_add', request_method='POST')
944 def emails_add(self):
943 def emails_add(self):
945 _ = self.request.translate
944 _ = self.request.translate
946 c = self.load_default_context()
945 c = self.load_default_context()
947
946
948 user_id = self.db_user_id
947 user_id = self.db_user_id
949 c.user = self.db_user
948 c.user = self.db_user
950
949
951 email = self.request.POST.get('new_email')
950 email = self.request.POST.get('new_email')
952 user_data = c.user.get_api_data()
951 user_data = c.user.get_api_data()
953 try:
952 try:
954
953
955 form = UserExtraEmailForm(self.request.translate)()
954 form = UserExtraEmailForm(self.request.translate)()
956 data = form.to_python({'email': email})
955 data = form.to_python({'email': email})
957 email = data['email']
956 email = data['email']
958
957
959 UserModel().add_extra_email(c.user.user_id, email)
958 UserModel().add_extra_email(c.user.user_id, email)
960 audit_logger.store_web(
959 audit_logger.store_web(
961 'user.edit.email.add',
960 'user.edit.email.add',
962 action_data={'email': email, 'user': user_data},
961 action_data={'email': email, 'user': user_data},
963 user=self._rhodecode_user)
962 user=self._rhodecode_user)
964 Session().commit()
963 Session().commit()
965 h.flash(_("Added new email address `%s` for user account") % email,
964 h.flash(_("Added new email address `%s` for user account") % email,
966 category='success')
965 category='success')
967 except formencode.Invalid as error:
966 except formencode.Invalid as error:
968 h.flash(h.escape(error.error_dict['email']), category='error')
967 h.flash(h.escape(error.error_dict['email']), category='error')
969 except IntegrityError:
968 except IntegrityError:
970 log.warning("Email %s already exists", email)
969 log.warning("Email %s already exists", email)
971 h.flash(_('Email `{}` is already registered for another user.').format(email),
970 h.flash(_('Email `{}` is already registered for another user.').format(email),
972 category='error')
971 category='error')
973 except Exception:
972 except Exception:
974 log.exception("Exception during email saving")
973 log.exception("Exception during email saving")
975 h.flash(_('An error occurred during email saving'),
974 h.flash(_('An error occurred during email saving'),
976 category='error')
975 category='error')
977 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
976 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
978
977
979 @LoginRequired()
978 @LoginRequired()
980 @HasPermissionAllDecorator('hg.admin')
979 @HasPermissionAllDecorator('hg.admin')
981 @CSRFRequired()
980 @CSRFRequired()
982 @view_config(
981 @view_config(
983 route_name='edit_user_emails_delete', request_method='POST')
982 route_name='edit_user_emails_delete', request_method='POST')
984 def emails_delete(self):
983 def emails_delete(self):
985 _ = self.request.translate
984 _ = self.request.translate
986 c = self.load_default_context()
985 c = self.load_default_context()
987
986
988 user_id = self.db_user_id
987 user_id = self.db_user_id
989 c.user = self.db_user
988 c.user = self.db_user
990
989
991 email_id = self.request.POST.get('del_email_id')
990 email_id = self.request.POST.get('del_email_id')
992 user_model = UserModel()
991 user_model = UserModel()
993
992
994 email = UserEmailMap.query().get(email_id).email
993 email = UserEmailMap.query().get(email_id).email
995 user_data = c.user.get_api_data()
994 user_data = c.user.get_api_data()
996 user_model.delete_extra_email(c.user.user_id, email_id)
995 user_model.delete_extra_email(c.user.user_id, email_id)
997 audit_logger.store_web(
996 audit_logger.store_web(
998 'user.edit.email.delete',
997 'user.edit.email.delete',
999 action_data={'email': email, 'user': user_data},
998 action_data={'email': email, 'user': user_data},
1000 user=self._rhodecode_user)
999 user=self._rhodecode_user)
1001 Session().commit()
1000 Session().commit()
1002 h.flash(_("Removed email address from user account"),
1001 h.flash(_("Removed email address from user account"),
1003 category='success')
1002 category='success')
1004 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1003 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1005
1004
1006 @LoginRequired()
1005 @LoginRequired()
1007 @HasPermissionAllDecorator('hg.admin')
1006 @HasPermissionAllDecorator('hg.admin')
1008 @view_config(
1007 @view_config(
1009 route_name='edit_user_ips', request_method='GET',
1008 route_name='edit_user_ips', request_method='GET',
1010 renderer='rhodecode:templates/admin/users/user_edit.mako')
1009 renderer='rhodecode:templates/admin/users/user_edit.mako')
1011 def ips(self):
1010 def ips(self):
1012 _ = self.request.translate
1011 _ = self.request.translate
1013 c = self.load_default_context()
1012 c = self.load_default_context()
1014 c.user = self.db_user
1013 c.user = self.db_user
1015
1014
1016 c.active = 'ips'
1015 c.active = 'ips'
1017 c.user_ip_map = UserIpMap.query() \
1016 c.user_ip_map = UserIpMap.query() \
1018 .filter(UserIpMap.user == c.user).all()
1017 .filter(UserIpMap.user == c.user).all()
1019
1018
1020 c.inherit_default_ips = c.user.inherit_default_permissions
1019 c.inherit_default_ips = c.user.inherit_default_permissions
1021 c.default_user_ip_map = UserIpMap.query() \
1020 c.default_user_ip_map = UserIpMap.query() \
1022 .filter(UserIpMap.user == User.get_default_user()).all()
1021 .filter(UserIpMap.user == User.get_default_user()).all()
1023
1022
1024 return self._get_template_context(c)
1023 return self._get_template_context(c)
1025
1024
1026 @LoginRequired()
1025 @LoginRequired()
1027 @HasPermissionAllDecorator('hg.admin')
1026 @HasPermissionAllDecorator('hg.admin')
1028 @CSRFRequired()
1027 @CSRFRequired()
1029 @view_config(
1028 @view_config(
1030 route_name='edit_user_ips_add', request_method='POST')
1029 route_name='edit_user_ips_add', request_method='POST')
1031 # NOTE(marcink): this view is allowed for default users, as we can
1030 # NOTE(marcink): this view is allowed for default users, as we can
1032 # edit their IP white list
1031 # edit their IP white list
1033 def ips_add(self):
1032 def ips_add(self):
1034 _ = self.request.translate
1033 _ = self.request.translate
1035 c = self.load_default_context()
1034 c = self.load_default_context()
1036
1035
1037 user_id = self.db_user_id
1036 user_id = self.db_user_id
1038 c.user = self.db_user
1037 c.user = self.db_user
1039
1038
1040 user_model = UserModel()
1039 user_model = UserModel()
1041 desc = self.request.POST.get('description')
1040 desc = self.request.POST.get('description')
1042 try:
1041 try:
1043 ip_list = user_model.parse_ip_range(
1042 ip_list = user_model.parse_ip_range(
1044 self.request.POST.get('new_ip'))
1043 self.request.POST.get('new_ip'))
1045 except Exception as e:
1044 except Exception as e:
1046 ip_list = []
1045 ip_list = []
1047 log.exception("Exception during ip saving")
1046 log.exception("Exception during ip saving")
1048 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1047 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1049 category='error')
1048 category='error')
1050 added = []
1049 added = []
1051 user_data = c.user.get_api_data()
1050 user_data = c.user.get_api_data()
1052 for ip in ip_list:
1051 for ip in ip_list:
1053 try:
1052 try:
1054 form = UserExtraIpForm(self.request.translate)()
1053 form = UserExtraIpForm(self.request.translate)()
1055 data = form.to_python({'ip': ip})
1054 data = form.to_python({'ip': ip})
1056 ip = data['ip']
1055 ip = data['ip']
1057
1056
1058 user_model.add_extra_ip(c.user.user_id, ip, desc)
1057 user_model.add_extra_ip(c.user.user_id, ip, desc)
1059 audit_logger.store_web(
1058 audit_logger.store_web(
1060 'user.edit.ip.add',
1059 'user.edit.ip.add',
1061 action_data={'ip': ip, 'user': user_data},
1060 action_data={'ip': ip, 'user': user_data},
1062 user=self._rhodecode_user)
1061 user=self._rhodecode_user)
1063 Session().commit()
1062 Session().commit()
1064 added.append(ip)
1063 added.append(ip)
1065 except formencode.Invalid as error:
1064 except formencode.Invalid as error:
1066 msg = error.error_dict['ip']
1065 msg = error.error_dict['ip']
1067 h.flash(msg, category='error')
1066 h.flash(msg, category='error')
1068 except Exception:
1067 except Exception:
1069 log.exception("Exception during ip saving")
1068 log.exception("Exception during ip saving")
1070 h.flash(_('An error occurred during ip saving'),
1069 h.flash(_('An error occurred during ip saving'),
1071 category='error')
1070 category='error')
1072 if added:
1071 if added:
1073 h.flash(
1072 h.flash(
1074 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1073 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1075 category='success')
1074 category='success')
1076 if 'default_user' in self.request.POST:
1075 if 'default_user' in self.request.POST:
1077 # case for editing global IP list we do it for 'DEFAULT' user
1076 # case for editing global IP list we do it for 'DEFAULT' user
1078 raise HTTPFound(h.route_path('admin_permissions_ips'))
1077 raise HTTPFound(h.route_path('admin_permissions_ips'))
1079 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1078 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1080
1079
1081 @LoginRequired()
1080 @LoginRequired()
1082 @HasPermissionAllDecorator('hg.admin')
1081 @HasPermissionAllDecorator('hg.admin')
1083 @CSRFRequired()
1082 @CSRFRequired()
1084 @view_config(
1083 @view_config(
1085 route_name='edit_user_ips_delete', request_method='POST')
1084 route_name='edit_user_ips_delete', request_method='POST')
1086 # NOTE(marcink): this view is allowed for default users, as we can
1085 # NOTE(marcink): this view is allowed for default users, as we can
1087 # edit their IP white list
1086 # edit their IP white list
1088 def ips_delete(self):
1087 def ips_delete(self):
1089 _ = self.request.translate
1088 _ = self.request.translate
1090 c = self.load_default_context()
1089 c = self.load_default_context()
1091
1090
1092 user_id = self.db_user_id
1091 user_id = self.db_user_id
1093 c.user = self.db_user
1092 c.user = self.db_user
1094
1093
1095 ip_id = self.request.POST.get('del_ip_id')
1094 ip_id = self.request.POST.get('del_ip_id')
1096 user_model = UserModel()
1095 user_model = UserModel()
1097 user_data = c.user.get_api_data()
1096 user_data = c.user.get_api_data()
1098 ip = UserIpMap.query().get(ip_id).ip_addr
1097 ip = UserIpMap.query().get(ip_id).ip_addr
1099 user_model.delete_extra_ip(c.user.user_id, ip_id)
1098 user_model.delete_extra_ip(c.user.user_id, ip_id)
1100 audit_logger.store_web(
1099 audit_logger.store_web(
1101 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1100 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1102 user=self._rhodecode_user)
1101 user=self._rhodecode_user)
1103 Session().commit()
1102 Session().commit()
1104 h.flash(_("Removed ip address from user whitelist"), category='success')
1103 h.flash(_("Removed ip address from user whitelist"), category='success')
1105
1104
1106 if 'default_user' in self.request.POST:
1105 if 'default_user' in self.request.POST:
1107 # case for editing global IP list we do it for 'DEFAULT' user
1106 # case for editing global IP list we do it for 'DEFAULT' user
1108 raise HTTPFound(h.route_path('admin_permissions_ips'))
1107 raise HTTPFound(h.route_path('admin_permissions_ips'))
1109 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1108 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1110
1109
1111 @LoginRequired()
1110 @LoginRequired()
1112 @HasPermissionAllDecorator('hg.admin')
1111 @HasPermissionAllDecorator('hg.admin')
1113 @view_config(
1112 @view_config(
1114 route_name='edit_user_groups_management', request_method='GET',
1113 route_name='edit_user_groups_management', request_method='GET',
1115 renderer='rhodecode:templates/admin/users/user_edit.mako')
1114 renderer='rhodecode:templates/admin/users/user_edit.mako')
1116 def groups_management(self):
1115 def groups_management(self):
1117 c = self.load_default_context()
1116 c = self.load_default_context()
1118 c.user = self.db_user
1117 c.user = self.db_user
1119 c.data = c.user.group_member
1118 c.data = c.user.group_member
1120
1119
1121 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1120 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1122 for group in c.user.group_member]
1121 for group in c.user.group_member]
1123 c.groups = json.dumps(groups)
1122 c.groups = json.dumps(groups)
1124 c.active = 'groups'
1123 c.active = 'groups'
1125
1124
1126 return self._get_template_context(c)
1125 return self._get_template_context(c)
1127
1126
1128 @LoginRequired()
1127 @LoginRequired()
1129 @HasPermissionAllDecorator('hg.admin')
1128 @HasPermissionAllDecorator('hg.admin')
1130 @CSRFRequired()
1129 @CSRFRequired()
1131 @view_config(
1130 @view_config(
1132 route_name='edit_user_groups_management_updates', request_method='POST')
1131 route_name='edit_user_groups_management_updates', request_method='POST')
1133 def groups_management_updates(self):
1132 def groups_management_updates(self):
1134 _ = self.request.translate
1133 _ = self.request.translate
1135 c = self.load_default_context()
1134 c = self.load_default_context()
1136
1135
1137 user_id = self.db_user_id
1136 user_id = self.db_user_id
1138 c.user = self.db_user
1137 c.user = self.db_user
1139
1138
1140 user_groups = set(self.request.POST.getall('users_group_id'))
1139 user_groups = set(self.request.POST.getall('users_group_id'))
1141 user_groups_objects = []
1140 user_groups_objects = []
1142
1141
1143 for ugid in user_groups:
1142 for ugid in user_groups:
1144 user_groups_objects.append(
1143 user_groups_objects.append(
1145 UserGroupModel().get_group(safe_int(ugid)))
1144 UserGroupModel().get_group(safe_int(ugid)))
1146 user_group_model = UserGroupModel()
1145 user_group_model = UserGroupModel()
1147 added_to_groups, removed_from_groups = \
1146 added_to_groups, removed_from_groups = \
1148 user_group_model.change_groups(c.user, user_groups_objects)
1147 user_group_model.change_groups(c.user, user_groups_objects)
1149
1148
1150 user_data = c.user.get_api_data()
1149 user_data = c.user.get_api_data()
1151 for user_group_id in added_to_groups:
1150 for user_group_id in added_to_groups:
1152 user_group = UserGroup.get(user_group_id)
1151 user_group = UserGroup.get(user_group_id)
1153 old_values = user_group.get_api_data()
1152 old_values = user_group.get_api_data()
1154 audit_logger.store_web(
1153 audit_logger.store_web(
1155 'user_group.edit.member.add',
1154 'user_group.edit.member.add',
1156 action_data={'user': user_data, 'old_data': old_values},
1155 action_data={'user': user_data, 'old_data': old_values},
1157 user=self._rhodecode_user)
1156 user=self._rhodecode_user)
1158
1157
1159 for user_group_id in removed_from_groups:
1158 for user_group_id in removed_from_groups:
1160 user_group = UserGroup.get(user_group_id)
1159 user_group = UserGroup.get(user_group_id)
1161 old_values = user_group.get_api_data()
1160 old_values = user_group.get_api_data()
1162 audit_logger.store_web(
1161 audit_logger.store_web(
1163 'user_group.edit.member.delete',
1162 'user_group.edit.member.delete',
1164 action_data={'user': user_data, 'old_data': old_values},
1163 action_data={'user': user_data, 'old_data': old_values},
1165 user=self._rhodecode_user)
1164 user=self._rhodecode_user)
1166
1165
1167 Session().commit()
1166 Session().commit()
1168 c.active = 'user_groups_management'
1167 c.active = 'user_groups_management'
1169 h.flash(_("Groups successfully changed"), category='success')
1168 h.flash(_("Groups successfully changed"), category='success')
1170
1169
1171 return HTTPFound(h.route_path(
1170 return HTTPFound(h.route_path(
1172 'edit_user_groups_management', user_id=user_id))
1171 'edit_user_groups_management', user_id=user_id))
1173
1172
1174 @LoginRequired()
1173 @LoginRequired()
1175 @HasPermissionAllDecorator('hg.admin')
1174 @HasPermissionAllDecorator('hg.admin')
1176 @view_config(
1175 @view_config(
1177 route_name='edit_user_audit_logs', request_method='GET',
1176 route_name='edit_user_audit_logs', request_method='GET',
1178 renderer='rhodecode:templates/admin/users/user_edit.mako')
1177 renderer='rhodecode:templates/admin/users/user_edit.mako')
1179 def user_audit_logs(self):
1178 def user_audit_logs(self):
1180 _ = self.request.translate
1179 _ = self.request.translate
1181 c = self.load_default_context()
1180 c = self.load_default_context()
1182 c.user = self.db_user
1181 c.user = self.db_user
1183
1182
1184 c.active = 'audit'
1183 c.active = 'audit'
1185
1184
1186 p = safe_int(self.request.GET.get('page', 1), 1)
1185 p = safe_int(self.request.GET.get('page', 1), 1)
1187
1186
1188 filter_term = self.request.GET.get('filter')
1187 filter_term = self.request.GET.get('filter')
1189 user_log = UserModel().get_user_log(c.user, filter_term)
1188 user_log = UserModel().get_user_log(c.user, filter_term)
1190
1189
1191 def url_generator(**kw):
1190 def url_generator(**kw):
1192 if filter_term:
1191 if filter_term:
1193 kw['filter'] = filter_term
1192 kw['filter'] = filter_term
1194 return self.request.current_route_path(_query=kw)
1193 return self.request.current_route_path(_query=kw)
1195
1194
1196 c.audit_logs = h.Page(
1195 c.audit_logs = h.Page(
1197 user_log, page=p, items_per_page=10, url=url_generator)
1196 user_log, page=p, items_per_page=10, url=url_generator)
1198 c.filter_term = filter_term
1197 c.filter_term = filter_term
1199 return self._get_template_context(c)
1198 return self._get_template_context(c)
1200
1199
1201 @LoginRequired()
1200 @LoginRequired()
1202 @HasPermissionAllDecorator('hg.admin')
1201 @HasPermissionAllDecorator('hg.admin')
1203 @view_config(
1202 @view_config(
1204 route_name='edit_user_perms_summary', request_method='GET',
1203 route_name='edit_user_perms_summary', request_method='GET',
1205 renderer='rhodecode:templates/admin/users/user_edit.mako')
1204 renderer='rhodecode:templates/admin/users/user_edit.mako')
1206 def user_perms_summary(self):
1205 def user_perms_summary(self):
1207 _ = self.request.translate
1206 _ = self.request.translate
1208 c = self.load_default_context()
1207 c = self.load_default_context()
1209 c.user = self.db_user
1208 c.user = self.db_user
1210
1209
1211 c.active = 'perms_summary'
1210 c.active = 'perms_summary'
1212 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1211 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1213
1212
1214 return self._get_template_context(c)
1213 return self._get_template_context(c)
1215
1214
1216 @LoginRequired()
1215 @LoginRequired()
1217 @HasPermissionAllDecorator('hg.admin')
1216 @HasPermissionAllDecorator('hg.admin')
1218 @view_config(
1217 @view_config(
1219 route_name='edit_user_perms_summary_json', request_method='GET',
1218 route_name='edit_user_perms_summary_json', request_method='GET',
1220 renderer='json_ext')
1219 renderer='json_ext')
1221 def user_perms_summary_json(self):
1220 def user_perms_summary_json(self):
1222 self.load_default_context()
1221 self.load_default_context()
1223 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1222 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1224
1223
1225 return perm_user.permissions
1224 return perm_user.permissions
1226
1225
1227 @LoginRequired()
1226 @LoginRequired()
1228 @HasPermissionAllDecorator('hg.admin')
1227 @HasPermissionAllDecorator('hg.admin')
1229 @view_config(
1228 @view_config(
1230 route_name='edit_user_caches', request_method='GET',
1229 route_name='edit_user_caches', request_method='GET',
1231 renderer='rhodecode:templates/admin/users/user_edit.mako')
1230 renderer='rhodecode:templates/admin/users/user_edit.mako')
1232 def user_caches(self):
1231 def user_caches(self):
1233 _ = self.request.translate
1232 _ = self.request.translate
1234 c = self.load_default_context()
1233 c = self.load_default_context()
1235 c.user = self.db_user
1234 c.user = self.db_user
1236
1235
1237 c.active = 'caches'
1236 c.active = 'caches'
1238 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1237 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1239
1238
1240 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1239 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1241 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1240 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1242 c.backend = c.region.backend
1241 c.backend = c.region.backend
1243 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1242 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1244
1243
1245 return self._get_template_context(c)
1244 return self._get_template_context(c)
1246
1245
1247 @LoginRequired()
1246 @LoginRequired()
1248 @HasPermissionAllDecorator('hg.admin')
1247 @HasPermissionAllDecorator('hg.admin')
1249 @CSRFRequired()
1248 @CSRFRequired()
1250 @view_config(
1249 @view_config(
1251 route_name='edit_user_caches_update', request_method='POST')
1250 route_name='edit_user_caches_update', request_method='POST')
1252 def user_caches_update(self):
1251 def user_caches_update(self):
1253 _ = self.request.translate
1252 _ = self.request.translate
1254 c = self.load_default_context()
1253 c = self.load_default_context()
1255 c.user = self.db_user
1254 c.user = self.db_user
1256
1255
1257 c.active = 'caches'
1256 c.active = 'caches'
1258 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1257 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1259
1258
1260 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1259 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1261 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1260 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1262
1261
1263 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1262 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1264
1263
1265 return HTTPFound(h.route_path(
1264 return HTTPFound(h.route_path(
1266 'edit_user_caches', user_id=c.user.user_id))
1265 'edit_user_caches', user_id=c.user.user_id))
@@ -1,777 +1,779 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2019 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 """
22 """
23 repo group model for RhodeCode
23 repo group model for RhodeCode
24 """
24 """
25
25
26 import os
26 import os
27 import datetime
27 import datetime
28 import itertools
28 import itertools
29 import logging
29 import logging
30 import shutil
30 import shutil
31 import traceback
31 import traceback
32 import string
32 import string
33
33
34 from zope.cachedescriptors.property import Lazy as LazyProperty
34 from zope.cachedescriptors.property import Lazy as LazyProperty
35
35
36 from rhodecode import events
36 from rhodecode import events
37 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
38 from rhodecode.model.db import (_hash_key,
38 from rhodecode.model.db import (_hash_key,
39 RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
39 RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
40 UserGroup, Repository)
40 UserGroup, Repository)
41 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
41 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
42 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.lib.caching_query import FromCache
43 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
43 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class RepoGroupModel(BaseModel):
48 class RepoGroupModel(BaseModel):
49
49
50 cls = RepoGroup
50 cls = RepoGroup
51 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
51 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
52 PERSONAL_GROUP_PATTERN = '${username}' # default
52 PERSONAL_GROUP_PATTERN = '${username}' # default
53
53
54 def _get_user_group(self, users_group):
54 def _get_user_group(self, users_group):
55 return self._get_instance(UserGroup, users_group,
55 return self._get_instance(UserGroup, users_group,
56 callback=UserGroup.get_by_group_name)
56 callback=UserGroup.get_by_group_name)
57
57
58 def _get_repo_group(self, repo_group):
58 def _get_repo_group(self, repo_group):
59 return self._get_instance(RepoGroup, repo_group,
59 return self._get_instance(RepoGroup, repo_group,
60 callback=RepoGroup.get_by_group_name)
60 callback=RepoGroup.get_by_group_name)
61
61
62 @LazyProperty
62 @LazyProperty
63 def repos_path(self):
63 def repos_path(self):
64 """
64 """
65 Gets the repositories root path from database
65 Gets the repositories root path from database
66 """
66 """
67
67
68 settings_model = VcsSettingsModel(sa=self.sa)
68 settings_model = VcsSettingsModel(sa=self.sa)
69 return settings_model.get_repos_location()
69 return settings_model.get_repos_location()
70
70
71 def get_by_group_name(self, repo_group_name, cache=None):
71 def get_by_group_name(self, repo_group_name, cache=None):
72 repo = self.sa.query(RepoGroup) \
72 repo = self.sa.query(RepoGroup) \
73 .filter(RepoGroup.group_name == repo_group_name)
73 .filter(RepoGroup.group_name == repo_group_name)
74
74
75 if cache:
75 if cache:
76 name_key = _hash_key(repo_group_name)
76 name_key = _hash_key(repo_group_name)
77 repo = repo.options(
77 repo = repo.options(
78 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
78 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
79 return repo.scalar()
79 return repo.scalar()
80
80
81 def get_default_create_personal_repo_group(self):
81 def get_default_create_personal_repo_group(self):
82 value = SettingsModel().get_setting_by_name(
82 value = SettingsModel().get_setting_by_name(
83 'create_personal_repo_group')
83 'create_personal_repo_group')
84 return value.app_settings_value if value else None or False
84 return value.app_settings_value if value else None or False
85
85
86 def get_personal_group_name_pattern(self):
86 def get_personal_group_name_pattern(self):
87 value = SettingsModel().get_setting_by_name(
87 value = SettingsModel().get_setting_by_name(
88 'personal_repo_group_pattern')
88 'personal_repo_group_pattern')
89 val = value.app_settings_value if value else None
89 val = value.app_settings_value if value else None
90 group_template = val or self.PERSONAL_GROUP_PATTERN
90 group_template = val or self.PERSONAL_GROUP_PATTERN
91
91
92 group_template = group_template.lstrip('/')
92 group_template = group_template.lstrip('/')
93 return group_template
93 return group_template
94
94
95 def get_personal_group_name(self, user):
95 def get_personal_group_name(self, user):
96 template = self.get_personal_group_name_pattern()
96 template = self.get_personal_group_name_pattern()
97 return string.Template(template).safe_substitute(
97 return string.Template(template).safe_substitute(
98 username=user.username,
98 username=user.username,
99 user_id=user.user_id,
99 user_id=user.user_id,
100 first_name=user.first_name,
101 last_name=user.last_name,
100 )
102 )
101
103
102 def create_personal_repo_group(self, user, commit_early=True):
104 def create_personal_repo_group(self, user, commit_early=True):
103 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
105 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
104 personal_repo_group_name = self.get_personal_group_name(user)
106 personal_repo_group_name = self.get_personal_group_name(user)
105
107
106 # create a new one
108 # create a new one
107 RepoGroupModel().create(
109 RepoGroupModel().create(
108 group_name=personal_repo_group_name,
110 group_name=personal_repo_group_name,
109 group_description=desc,
111 group_description=desc,
110 owner=user.username,
112 owner=user.username,
111 personal=True,
113 personal=True,
112 commit_early=commit_early)
114 commit_early=commit_early)
113
115
114 def _create_default_perms(self, new_group):
116 def _create_default_perms(self, new_group):
115 # create default permission
117 # create default permission
116 default_perm = 'group.read'
118 default_perm = 'group.read'
117 def_user = User.get_default_user()
119 def_user = User.get_default_user()
118 for p in def_user.user_perms:
120 for p in def_user.user_perms:
119 if p.permission.permission_name.startswith('group.'):
121 if p.permission.permission_name.startswith('group.'):
120 default_perm = p.permission.permission_name
122 default_perm = p.permission.permission_name
121 break
123 break
122
124
123 repo_group_to_perm = UserRepoGroupToPerm()
125 repo_group_to_perm = UserRepoGroupToPerm()
124 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
126 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
125
127
126 repo_group_to_perm.group = new_group
128 repo_group_to_perm.group = new_group
127 repo_group_to_perm.user_id = def_user.user_id
129 repo_group_to_perm.user_id = def_user.user_id
128 return repo_group_to_perm
130 return repo_group_to_perm
129
131
130 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
132 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
131 get_object=False):
133 get_object=False):
132 """
134 """
133 Get's the group name and a parent group name from given group name.
135 Get's the group name and a parent group name from given group name.
134 If repo_in_path is set to truth, we asume the full path also includes
136 If repo_in_path is set to truth, we asume the full path also includes
135 repo name, in such case we clean the last element.
137 repo name, in such case we clean the last element.
136
138
137 :param group_name_full:
139 :param group_name_full:
138 """
140 """
139 split_paths = 1
141 split_paths = 1
140 if repo_in_path:
142 if repo_in_path:
141 split_paths = 2
143 split_paths = 2
142 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
144 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
143
145
144 if repo_in_path and len(_parts) > 1:
146 if repo_in_path and len(_parts) > 1:
145 # such case last element is the repo_name
147 # such case last element is the repo_name
146 _parts.pop(-1)
148 _parts.pop(-1)
147 group_name_cleaned = _parts[-1] # just the group name
149 group_name_cleaned = _parts[-1] # just the group name
148 parent_repo_group_name = None
150 parent_repo_group_name = None
149
151
150 if len(_parts) > 1:
152 if len(_parts) > 1:
151 parent_repo_group_name = _parts[0]
153 parent_repo_group_name = _parts[0]
152
154
153 parent_group = None
155 parent_group = None
154 if parent_repo_group_name:
156 if parent_repo_group_name:
155 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
157 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
156
158
157 if get_object:
159 if get_object:
158 return group_name_cleaned, parent_repo_group_name, parent_group
160 return group_name_cleaned, parent_repo_group_name, parent_group
159
161
160 return group_name_cleaned, parent_repo_group_name
162 return group_name_cleaned, parent_repo_group_name
161
163
162 def check_exist_filesystem(self, group_name, exc_on_failure=True):
164 def check_exist_filesystem(self, group_name, exc_on_failure=True):
163 create_path = os.path.join(self.repos_path, group_name)
165 create_path = os.path.join(self.repos_path, group_name)
164 log.debug('creating new group in %s', create_path)
166 log.debug('creating new group in %s', create_path)
165
167
166 if os.path.isdir(create_path):
168 if os.path.isdir(create_path):
167 if exc_on_failure:
169 if exc_on_failure:
168 abs_create_path = os.path.abspath(create_path)
170 abs_create_path = os.path.abspath(create_path)
169 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
171 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
170 return False
172 return False
171 return True
173 return True
172
174
173 def _create_group(self, group_name):
175 def _create_group(self, group_name):
174 """
176 """
175 makes repository group on filesystem
177 makes repository group on filesystem
176
178
177 :param repo_name:
179 :param repo_name:
178 :param parent_id:
180 :param parent_id:
179 """
181 """
180
182
181 self.check_exist_filesystem(group_name)
183 self.check_exist_filesystem(group_name)
182 create_path = os.path.join(self.repos_path, group_name)
184 create_path = os.path.join(self.repos_path, group_name)
183 log.debug('creating new group in %s', create_path)
185 log.debug('creating new group in %s', create_path)
184 os.makedirs(create_path, mode=0o755)
186 os.makedirs(create_path, mode=0o755)
185 log.debug('created group in %s', create_path)
187 log.debug('created group in %s', create_path)
186
188
187 def _rename_group(self, old, new):
189 def _rename_group(self, old, new):
188 """
190 """
189 Renames a group on filesystem
191 Renames a group on filesystem
190
192
191 :param group_name:
193 :param group_name:
192 """
194 """
193
195
194 if old == new:
196 if old == new:
195 log.debug('skipping group rename')
197 log.debug('skipping group rename')
196 return
198 return
197
199
198 log.debug('renaming repository group from %s to %s', old, new)
200 log.debug('renaming repository group from %s to %s', old, new)
199
201
200 old_path = os.path.join(self.repos_path, old)
202 old_path = os.path.join(self.repos_path, old)
201 new_path = os.path.join(self.repos_path, new)
203 new_path = os.path.join(self.repos_path, new)
202
204
203 log.debug('renaming repos paths from %s to %s', old_path, new_path)
205 log.debug('renaming repos paths from %s to %s', old_path, new_path)
204
206
205 if os.path.isdir(new_path):
207 if os.path.isdir(new_path):
206 raise Exception('Was trying to rename to already '
208 raise Exception('Was trying to rename to already '
207 'existing dir %s' % new_path)
209 'existing dir %s' % new_path)
208 shutil.move(old_path, new_path)
210 shutil.move(old_path, new_path)
209
211
210 def _delete_filesystem_group(self, group, force_delete=False):
212 def _delete_filesystem_group(self, group, force_delete=False):
211 """
213 """
212 Deletes a group from a filesystem
214 Deletes a group from a filesystem
213
215
214 :param group: instance of group from database
216 :param group: instance of group from database
215 :param force_delete: use shutil rmtree to remove all objects
217 :param force_delete: use shutil rmtree to remove all objects
216 """
218 """
217 paths = group.full_path.split(RepoGroup.url_sep())
219 paths = group.full_path.split(RepoGroup.url_sep())
218 paths = os.sep.join(paths)
220 paths = os.sep.join(paths)
219
221
220 rm_path = os.path.join(self.repos_path, paths)
222 rm_path = os.path.join(self.repos_path, paths)
221 log.info("Removing group %s", rm_path)
223 log.info("Removing group %s", rm_path)
222 # delete only if that path really exists
224 # delete only if that path really exists
223 if os.path.isdir(rm_path):
225 if os.path.isdir(rm_path):
224 if force_delete:
226 if force_delete:
225 shutil.rmtree(rm_path)
227 shutil.rmtree(rm_path)
226 else:
228 else:
227 # archive that group`
229 # archive that group`
228 _now = datetime.datetime.now()
230 _now = datetime.datetime.now()
229 _ms = str(_now.microsecond).rjust(6, '0')
231 _ms = str(_now.microsecond).rjust(6, '0')
230 _d = 'rm__%s_GROUP_%s' % (
232 _d = 'rm__%s_GROUP_%s' % (
231 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
233 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
232 shutil.move(rm_path, os.path.join(self.repos_path, _d))
234 shutil.move(rm_path, os.path.join(self.repos_path, _d))
233
235
234 def create(self, group_name, group_description, owner, just_db=False,
236 def create(self, group_name, group_description, owner, just_db=False,
235 copy_permissions=False, personal=None, commit_early=True):
237 copy_permissions=False, personal=None, commit_early=True):
236
238
237 (group_name_cleaned,
239 (group_name_cleaned,
238 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
240 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
239
241
240 parent_group = None
242 parent_group = None
241 if parent_group_name:
243 if parent_group_name:
242 parent_group = self._get_repo_group(parent_group_name)
244 parent_group = self._get_repo_group(parent_group_name)
243 if not parent_group:
245 if not parent_group:
244 # we tried to create a nested group, but the parent is not
246 # we tried to create a nested group, but the parent is not
245 # existing
247 # existing
246 raise ValueError(
248 raise ValueError(
247 'Parent group `%s` given in `%s` group name '
249 'Parent group `%s` given in `%s` group name '
248 'is not yet existing.' % (parent_group_name, group_name))
250 'is not yet existing.' % (parent_group_name, group_name))
249
251
250 # because we are doing a cleanup, we need to check if such directory
252 # because we are doing a cleanup, we need to check if such directory
251 # already exists. If we don't do that we can accidentally delete
253 # already exists. If we don't do that we can accidentally delete
252 # existing directory via cleanup that can cause data issues, since
254 # existing directory via cleanup that can cause data issues, since
253 # delete does a folder rename to special syntax later cleanup
255 # delete does a folder rename to special syntax later cleanup
254 # functions can delete this
256 # functions can delete this
255 cleanup_group = self.check_exist_filesystem(group_name,
257 cleanup_group = self.check_exist_filesystem(group_name,
256 exc_on_failure=False)
258 exc_on_failure=False)
257 user = self._get_user(owner)
259 user = self._get_user(owner)
258 if not user:
260 if not user:
259 raise ValueError('Owner %s not found as rhodecode user', owner)
261 raise ValueError('Owner %s not found as rhodecode user', owner)
260
262
261 try:
263 try:
262 new_repo_group = RepoGroup()
264 new_repo_group = RepoGroup()
263 new_repo_group.user = user
265 new_repo_group.user = user
264 new_repo_group.group_description = group_description or group_name
266 new_repo_group.group_description = group_description or group_name
265 new_repo_group.parent_group = parent_group
267 new_repo_group.parent_group = parent_group
266 new_repo_group.group_name = group_name
268 new_repo_group.group_name = group_name
267 new_repo_group.personal = personal
269 new_repo_group.personal = personal
268
270
269 self.sa.add(new_repo_group)
271 self.sa.add(new_repo_group)
270
272
271 # create an ADMIN permission for owner except if we're super admin,
273 # create an ADMIN permission for owner except if we're super admin,
272 # later owner should go into the owner field of groups
274 # later owner should go into the owner field of groups
273 if not user.is_admin:
275 if not user.is_admin:
274 self.grant_user_permission(repo_group=new_repo_group,
276 self.grant_user_permission(repo_group=new_repo_group,
275 user=owner, perm='group.admin')
277 user=owner, perm='group.admin')
276
278
277 if parent_group and copy_permissions:
279 if parent_group and copy_permissions:
278 # copy permissions from parent
280 # copy permissions from parent
279 user_perms = UserRepoGroupToPerm.query() \
281 user_perms = UserRepoGroupToPerm.query() \
280 .filter(UserRepoGroupToPerm.group == parent_group).all()
282 .filter(UserRepoGroupToPerm.group == parent_group).all()
281
283
282 group_perms = UserGroupRepoGroupToPerm.query() \
284 group_perms = UserGroupRepoGroupToPerm.query() \
283 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
285 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
284
286
285 for perm in user_perms:
287 for perm in user_perms:
286 # don't copy over the permission for user who is creating
288 # don't copy over the permission for user who is creating
287 # this group, if he is not super admin he get's admin
289 # this group, if he is not super admin he get's admin
288 # permission set above
290 # permission set above
289 if perm.user != user or user.is_admin:
291 if perm.user != user or user.is_admin:
290 UserRepoGroupToPerm.create(
292 UserRepoGroupToPerm.create(
291 perm.user, new_repo_group, perm.permission)
293 perm.user, new_repo_group, perm.permission)
292
294
293 for perm in group_perms:
295 for perm in group_perms:
294 UserGroupRepoGroupToPerm.create(
296 UserGroupRepoGroupToPerm.create(
295 perm.users_group, new_repo_group, perm.permission)
297 perm.users_group, new_repo_group, perm.permission)
296 else:
298 else:
297 perm_obj = self._create_default_perms(new_repo_group)
299 perm_obj = self._create_default_perms(new_repo_group)
298 self.sa.add(perm_obj)
300 self.sa.add(perm_obj)
299
301
300 # now commit the changes, earlier so we are sure everything is in
302 # now commit the changes, earlier so we are sure everything is in
301 # the database.
303 # the database.
302 if commit_early:
304 if commit_early:
303 self.sa.commit()
305 self.sa.commit()
304 if not just_db:
306 if not just_db:
305 self._create_group(new_repo_group.group_name)
307 self._create_group(new_repo_group.group_name)
306
308
307 # trigger the post hook
309 # trigger the post hook
308 from rhodecode.lib.hooks_base import log_create_repository_group
310 from rhodecode.lib.hooks_base import log_create_repository_group
309 repo_group = RepoGroup.get_by_group_name(group_name)
311 repo_group = RepoGroup.get_by_group_name(group_name)
310 log_create_repository_group(
312 log_create_repository_group(
311 created_by=user.username, **repo_group.get_dict())
313 created_by=user.username, **repo_group.get_dict())
312
314
313 # Trigger create event.
315 # Trigger create event.
314 events.trigger(events.RepoGroupCreateEvent(repo_group))
316 events.trigger(events.RepoGroupCreateEvent(repo_group))
315
317
316 return new_repo_group
318 return new_repo_group
317 except Exception:
319 except Exception:
318 self.sa.rollback()
320 self.sa.rollback()
319 log.exception('Exception occurred when creating repository group, '
321 log.exception('Exception occurred when creating repository group, '
320 'doing cleanup...')
322 'doing cleanup...')
321 # rollback things manually !
323 # rollback things manually !
322 repo_group = RepoGroup.get_by_group_name(group_name)
324 repo_group = RepoGroup.get_by_group_name(group_name)
323 if repo_group:
325 if repo_group:
324 RepoGroup.delete(repo_group.group_id)
326 RepoGroup.delete(repo_group.group_id)
325 self.sa.commit()
327 self.sa.commit()
326 if cleanup_group:
328 if cleanup_group:
327 RepoGroupModel()._delete_filesystem_group(repo_group)
329 RepoGroupModel()._delete_filesystem_group(repo_group)
328 raise
330 raise
329
331
330 def update_permissions(
332 def update_permissions(
331 self, repo_group, perm_additions=None, perm_updates=None,
333 self, repo_group, perm_additions=None, perm_updates=None,
332 perm_deletions=None, recursive=None, check_perms=True,
334 perm_deletions=None, recursive=None, check_perms=True,
333 cur_user=None):
335 cur_user=None):
334 from rhodecode.model.repo import RepoModel
336 from rhodecode.model.repo import RepoModel
335 from rhodecode.lib.auth import HasUserGroupPermissionAny
337 from rhodecode.lib.auth import HasUserGroupPermissionAny
336
338
337 if not perm_additions:
339 if not perm_additions:
338 perm_additions = []
340 perm_additions = []
339 if not perm_updates:
341 if not perm_updates:
340 perm_updates = []
342 perm_updates = []
341 if not perm_deletions:
343 if not perm_deletions:
342 perm_deletions = []
344 perm_deletions = []
343
345
344 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
346 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
345
347
346 changes = {
348 changes = {
347 'added': [],
349 'added': [],
348 'updated': [],
350 'updated': [],
349 'deleted': []
351 'deleted': []
350 }
352 }
351
353
352 def _set_perm_user(obj, user, perm):
354 def _set_perm_user(obj, user, perm):
353 if isinstance(obj, RepoGroup):
355 if isinstance(obj, RepoGroup):
354 self.grant_user_permission(
356 self.grant_user_permission(
355 repo_group=obj, user=user, perm=perm)
357 repo_group=obj, user=user, perm=perm)
356 elif isinstance(obj, Repository):
358 elif isinstance(obj, Repository):
357 # private repos will not allow to change the default
359 # private repos will not allow to change the default
358 # permissions using recursive mode
360 # permissions using recursive mode
359 if obj.private and user == User.DEFAULT_USER:
361 if obj.private and user == User.DEFAULT_USER:
360 return
362 return
361
363
362 # we set group permission but we have to switch to repo
364 # we set group permission but we have to switch to repo
363 # permission
365 # permission
364 perm = perm.replace('group.', 'repository.')
366 perm = perm.replace('group.', 'repository.')
365 RepoModel().grant_user_permission(
367 RepoModel().grant_user_permission(
366 repo=obj, user=user, perm=perm)
368 repo=obj, user=user, perm=perm)
367
369
368 def _set_perm_group(obj, users_group, perm):
370 def _set_perm_group(obj, users_group, perm):
369 if isinstance(obj, RepoGroup):
371 if isinstance(obj, RepoGroup):
370 self.grant_user_group_permission(
372 self.grant_user_group_permission(
371 repo_group=obj, group_name=users_group, perm=perm)
373 repo_group=obj, group_name=users_group, perm=perm)
372 elif isinstance(obj, Repository):
374 elif isinstance(obj, Repository):
373 # we set group permission but we have to switch to repo
375 # we set group permission but we have to switch to repo
374 # permission
376 # permission
375 perm = perm.replace('group.', 'repository.')
377 perm = perm.replace('group.', 'repository.')
376 RepoModel().grant_user_group_permission(
378 RepoModel().grant_user_group_permission(
377 repo=obj, group_name=users_group, perm=perm)
379 repo=obj, group_name=users_group, perm=perm)
378
380
379 def _revoke_perm_user(obj, user):
381 def _revoke_perm_user(obj, user):
380 if isinstance(obj, RepoGroup):
382 if isinstance(obj, RepoGroup):
381 self.revoke_user_permission(repo_group=obj, user=user)
383 self.revoke_user_permission(repo_group=obj, user=user)
382 elif isinstance(obj, Repository):
384 elif isinstance(obj, Repository):
383 RepoModel().revoke_user_permission(repo=obj, user=user)
385 RepoModel().revoke_user_permission(repo=obj, user=user)
384
386
385 def _revoke_perm_group(obj, user_group):
387 def _revoke_perm_group(obj, user_group):
386 if isinstance(obj, RepoGroup):
388 if isinstance(obj, RepoGroup):
387 self.revoke_user_group_permission(
389 self.revoke_user_group_permission(
388 repo_group=obj, group_name=user_group)
390 repo_group=obj, group_name=user_group)
389 elif isinstance(obj, Repository):
391 elif isinstance(obj, Repository):
390 RepoModel().revoke_user_group_permission(
392 RepoModel().revoke_user_group_permission(
391 repo=obj, group_name=user_group)
393 repo=obj, group_name=user_group)
392
394
393 # start updates
395 # start updates
394 log.debug('Now updating permissions for %s in recursive mode:%s',
396 log.debug('Now updating permissions for %s in recursive mode:%s',
395 repo_group, recursive)
397 repo_group, recursive)
396
398
397 # initialize check function, we'll call that multiple times
399 # initialize check function, we'll call that multiple times
398 has_group_perm = HasUserGroupPermissionAny(*req_perms)
400 has_group_perm = HasUserGroupPermissionAny(*req_perms)
399
401
400 for obj in repo_group.recursive_groups_and_repos():
402 for obj in repo_group.recursive_groups_and_repos():
401 # iterated obj is an instance of a repos group or repository in
403 # iterated obj is an instance of a repos group or repository in
402 # that group, recursive option can be: none, repos, groups, all
404 # that group, recursive option can be: none, repos, groups, all
403 if recursive == 'all':
405 if recursive == 'all':
404 obj = obj
406 obj = obj
405 elif recursive == 'repos':
407 elif recursive == 'repos':
406 # skip groups, other than this one
408 # skip groups, other than this one
407 if isinstance(obj, RepoGroup) and not obj == repo_group:
409 if isinstance(obj, RepoGroup) and not obj == repo_group:
408 continue
410 continue
409 elif recursive == 'groups':
411 elif recursive == 'groups':
410 # skip repos
412 # skip repos
411 if isinstance(obj, Repository):
413 if isinstance(obj, Repository):
412 continue
414 continue
413 else: # recursive == 'none':
415 else: # recursive == 'none':
414 # DEFAULT option - don't apply to iterated objects
416 # DEFAULT option - don't apply to iterated objects
415 # also we do a break at the end of this loop. if we are not
417 # also we do a break at the end of this loop. if we are not
416 # in recursive mode
418 # in recursive mode
417 obj = repo_group
419 obj = repo_group
418
420
419 change_obj = obj.get_api_data()
421 change_obj = obj.get_api_data()
420
422
421 # update permissions
423 # update permissions
422 for member_id, perm, member_type in perm_updates:
424 for member_id, perm, member_type in perm_updates:
423 member_id = int(member_id)
425 member_id = int(member_id)
424 if member_type == 'user':
426 if member_type == 'user':
425 member_name = User.get(member_id).username
427 member_name = User.get(member_id).username
426 # this updates also current one if found
428 # this updates also current one if found
427 _set_perm_user(obj, user=member_id, perm=perm)
429 _set_perm_user(obj, user=member_id, perm=perm)
428 elif member_type == 'user_group':
430 elif member_type == 'user_group':
429 member_name = UserGroup.get(member_id).users_group_name
431 member_name = UserGroup.get(member_id).users_group_name
430 if not check_perms or has_group_perm(member_name,
432 if not check_perms or has_group_perm(member_name,
431 user=cur_user):
433 user=cur_user):
432 _set_perm_group(obj, users_group=member_id, perm=perm)
434 _set_perm_group(obj, users_group=member_id, perm=perm)
433 else:
435 else:
434 raise ValueError("member_type must be 'user' or 'user_group' "
436 raise ValueError("member_type must be 'user' or 'user_group' "
435 "got {} instead".format(member_type))
437 "got {} instead".format(member_type))
436
438
437 changes['updated'].append(
439 changes['updated'].append(
438 {'change_obj': change_obj, 'type': member_type,
440 {'change_obj': change_obj, 'type': member_type,
439 'id': member_id, 'name': member_name, 'new_perm': perm})
441 'id': member_id, 'name': member_name, 'new_perm': perm})
440
442
441 # set new permissions
443 # set new permissions
442 for member_id, perm, member_type in perm_additions:
444 for member_id, perm, member_type in perm_additions:
443 member_id = int(member_id)
445 member_id = int(member_id)
444 if member_type == 'user':
446 if member_type == 'user':
445 member_name = User.get(member_id).username
447 member_name = User.get(member_id).username
446 _set_perm_user(obj, user=member_id, perm=perm)
448 _set_perm_user(obj, user=member_id, perm=perm)
447 elif member_type == 'user_group':
449 elif member_type == 'user_group':
448 # check if we have permissions to alter this usergroup
450 # check if we have permissions to alter this usergroup
449 member_name = UserGroup.get(member_id).users_group_name
451 member_name = UserGroup.get(member_id).users_group_name
450 if not check_perms or has_group_perm(member_name,
452 if not check_perms or has_group_perm(member_name,
451 user=cur_user):
453 user=cur_user):
452 _set_perm_group(obj, users_group=member_id, perm=perm)
454 _set_perm_group(obj, users_group=member_id, perm=perm)
453 else:
455 else:
454 raise ValueError("member_type must be 'user' or 'user_group' "
456 raise ValueError("member_type must be 'user' or 'user_group' "
455 "got {} instead".format(member_type))
457 "got {} instead".format(member_type))
456
458
457 changes['added'].append(
459 changes['added'].append(
458 {'change_obj': change_obj, 'type': member_type,
460 {'change_obj': change_obj, 'type': member_type,
459 'id': member_id, 'name': member_name, 'new_perm': perm})
461 'id': member_id, 'name': member_name, 'new_perm': perm})
460
462
461 # delete permissions
463 # delete permissions
462 for member_id, perm, member_type in perm_deletions:
464 for member_id, perm, member_type in perm_deletions:
463 member_id = int(member_id)
465 member_id = int(member_id)
464 if member_type == 'user':
466 if member_type == 'user':
465 member_name = User.get(member_id).username
467 member_name = User.get(member_id).username
466 _revoke_perm_user(obj, user=member_id)
468 _revoke_perm_user(obj, user=member_id)
467 elif member_type == 'user_group':
469 elif member_type == 'user_group':
468 # check if we have permissions to alter this usergroup
470 # check if we have permissions to alter this usergroup
469 member_name = UserGroup.get(member_id).users_group_name
471 member_name = UserGroup.get(member_id).users_group_name
470 if not check_perms or has_group_perm(member_name,
472 if not check_perms or has_group_perm(member_name,
471 user=cur_user):
473 user=cur_user):
472 _revoke_perm_group(obj, user_group=member_id)
474 _revoke_perm_group(obj, user_group=member_id)
473 else:
475 else:
474 raise ValueError("member_type must be 'user' or 'user_group' "
476 raise ValueError("member_type must be 'user' or 'user_group' "
475 "got {} instead".format(member_type))
477 "got {} instead".format(member_type))
476
478
477 changes['deleted'].append(
479 changes['deleted'].append(
478 {'change_obj': change_obj, 'type': member_type,
480 {'change_obj': change_obj, 'type': member_type,
479 'id': member_id, 'name': member_name, 'new_perm': perm})
481 'id': member_id, 'name': member_name, 'new_perm': perm})
480
482
481 # if it's not recursive call for all,repos,groups
483 # if it's not recursive call for all,repos,groups
482 # break the loop and don't proceed with other changes
484 # break the loop and don't proceed with other changes
483 if recursive not in ['all', 'repos', 'groups']:
485 if recursive not in ['all', 'repos', 'groups']:
484 break
486 break
485
487
486 return changes
488 return changes
487
489
488 def update(self, repo_group, form_data):
490 def update(self, repo_group, form_data):
489 try:
491 try:
490 repo_group = self._get_repo_group(repo_group)
492 repo_group = self._get_repo_group(repo_group)
491 old_path = repo_group.full_path
493 old_path = repo_group.full_path
492
494
493 # change properties
495 # change properties
494 if 'group_description' in form_data:
496 if 'group_description' in form_data:
495 repo_group.group_description = form_data['group_description']
497 repo_group.group_description = form_data['group_description']
496
498
497 if 'enable_locking' in form_data:
499 if 'enable_locking' in form_data:
498 repo_group.enable_locking = form_data['enable_locking']
500 repo_group.enable_locking = form_data['enable_locking']
499
501
500 if 'group_parent_id' in form_data:
502 if 'group_parent_id' in form_data:
501 parent_group = (
503 parent_group = (
502 self._get_repo_group(form_data['group_parent_id']))
504 self._get_repo_group(form_data['group_parent_id']))
503 repo_group.group_parent_id = (
505 repo_group.group_parent_id = (
504 parent_group.group_id if parent_group else None)
506 parent_group.group_id if parent_group else None)
505 repo_group.parent_group = parent_group
507 repo_group.parent_group = parent_group
506
508
507 # mikhail: to update the full_path, we have to explicitly
509 # mikhail: to update the full_path, we have to explicitly
508 # update group_name
510 # update group_name
509 group_name = form_data.get('group_name', repo_group.name)
511 group_name = form_data.get('group_name', repo_group.name)
510 repo_group.group_name = repo_group.get_new_name(group_name)
512 repo_group.group_name = repo_group.get_new_name(group_name)
511
513
512 new_path = repo_group.full_path
514 new_path = repo_group.full_path
513
515
514 if 'user' in form_data:
516 if 'user' in form_data:
515 repo_group.user = User.get_by_username(form_data['user'])
517 repo_group.user = User.get_by_username(form_data['user'])
516 repo_group.updated_on = datetime.datetime.now()
518 repo_group.updated_on = datetime.datetime.now()
517 self.sa.add(repo_group)
519 self.sa.add(repo_group)
518
520
519 # iterate over all members of this groups and do fixes
521 # iterate over all members of this groups and do fixes
520 # set locking if given
522 # set locking if given
521 # if obj is a repoGroup also fix the name of the group according
523 # if obj is a repoGroup also fix the name of the group according
522 # to the parent
524 # to the parent
523 # if obj is a Repo fix it's name
525 # if obj is a Repo fix it's name
524 # this can be potentially heavy operation
526 # this can be potentially heavy operation
525 for obj in repo_group.recursive_groups_and_repos():
527 for obj in repo_group.recursive_groups_and_repos():
526 # set the value from it's parent
528 # set the value from it's parent
527 obj.enable_locking = repo_group.enable_locking
529 obj.enable_locking = repo_group.enable_locking
528 if isinstance(obj, RepoGroup):
530 if isinstance(obj, RepoGroup):
529 new_name = obj.get_new_name(obj.name)
531 new_name = obj.get_new_name(obj.name)
530 log.debug('Fixing group %s to new name %s',
532 log.debug('Fixing group %s to new name %s',
531 obj.group_name, new_name)
533 obj.group_name, new_name)
532 obj.group_name = new_name
534 obj.group_name = new_name
533 obj.updated_on = datetime.datetime.now()
535 obj.updated_on = datetime.datetime.now()
534 elif isinstance(obj, Repository):
536 elif isinstance(obj, Repository):
535 # we need to get all repositories from this new group and
537 # we need to get all repositories from this new group and
536 # rename them accordingly to new group path
538 # rename them accordingly to new group path
537 new_name = obj.get_new_name(obj.just_name)
539 new_name = obj.get_new_name(obj.just_name)
538 log.debug('Fixing repo %s to new name %s',
540 log.debug('Fixing repo %s to new name %s',
539 obj.repo_name, new_name)
541 obj.repo_name, new_name)
540 obj.repo_name = new_name
542 obj.repo_name = new_name
541 obj.updated_on = datetime.datetime.now()
543 obj.updated_on = datetime.datetime.now()
542 self.sa.add(obj)
544 self.sa.add(obj)
543
545
544 self._rename_group(old_path, new_path)
546 self._rename_group(old_path, new_path)
545
547
546 # Trigger update event.
548 # Trigger update event.
547 events.trigger(events.RepoGroupUpdateEvent(repo_group))
549 events.trigger(events.RepoGroupUpdateEvent(repo_group))
548
550
549 return repo_group
551 return repo_group
550 except Exception:
552 except Exception:
551 log.error(traceback.format_exc())
553 log.error(traceback.format_exc())
552 raise
554 raise
553
555
554 def delete(self, repo_group, force_delete=False, fs_remove=True):
556 def delete(self, repo_group, force_delete=False, fs_remove=True):
555 repo_group = self._get_repo_group(repo_group)
557 repo_group = self._get_repo_group(repo_group)
556 if not repo_group:
558 if not repo_group:
557 return False
559 return False
558 try:
560 try:
559 self.sa.delete(repo_group)
561 self.sa.delete(repo_group)
560 if fs_remove:
562 if fs_remove:
561 self._delete_filesystem_group(repo_group, force_delete)
563 self._delete_filesystem_group(repo_group, force_delete)
562 else:
564 else:
563 log.debug('skipping removal from filesystem')
565 log.debug('skipping removal from filesystem')
564
566
565 # Trigger delete event.
567 # Trigger delete event.
566 events.trigger(events.RepoGroupDeleteEvent(repo_group))
568 events.trigger(events.RepoGroupDeleteEvent(repo_group))
567 return True
569 return True
568
570
569 except Exception:
571 except Exception:
570 log.error('Error removing repo_group %s', repo_group)
572 log.error('Error removing repo_group %s', repo_group)
571 raise
573 raise
572
574
573 def grant_user_permission(self, repo_group, user, perm):
575 def grant_user_permission(self, repo_group, user, perm):
574 """
576 """
575 Grant permission for user on given repository group, or update
577 Grant permission for user on given repository group, or update
576 existing one if found
578 existing one if found
577
579
578 :param repo_group: Instance of RepoGroup, repositories_group_id,
580 :param repo_group: Instance of RepoGroup, repositories_group_id,
579 or repositories_group name
581 or repositories_group name
580 :param user: Instance of User, user_id or username
582 :param user: Instance of User, user_id or username
581 :param perm: Instance of Permission, or permission_name
583 :param perm: Instance of Permission, or permission_name
582 """
584 """
583
585
584 repo_group = self._get_repo_group(repo_group)
586 repo_group = self._get_repo_group(repo_group)
585 user = self._get_user(user)
587 user = self._get_user(user)
586 permission = self._get_perm(perm)
588 permission = self._get_perm(perm)
587
589
588 # check if we have that permission already
590 # check if we have that permission already
589 obj = self.sa.query(UserRepoGroupToPerm)\
591 obj = self.sa.query(UserRepoGroupToPerm)\
590 .filter(UserRepoGroupToPerm.user == user)\
592 .filter(UserRepoGroupToPerm.user == user)\
591 .filter(UserRepoGroupToPerm.group == repo_group)\
593 .filter(UserRepoGroupToPerm.group == repo_group)\
592 .scalar()
594 .scalar()
593 if obj is None:
595 if obj is None:
594 # create new !
596 # create new !
595 obj = UserRepoGroupToPerm()
597 obj = UserRepoGroupToPerm()
596 obj.group = repo_group
598 obj.group = repo_group
597 obj.user = user
599 obj.user = user
598 obj.permission = permission
600 obj.permission = permission
599 self.sa.add(obj)
601 self.sa.add(obj)
600 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
602 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
601 action_logger_generic(
603 action_logger_generic(
602 'granted permission: {} to user: {} on repogroup: {}'.format(
604 'granted permission: {} to user: {} on repogroup: {}'.format(
603 perm, user, repo_group), namespace='security.repogroup')
605 perm, user, repo_group), namespace='security.repogroup')
604 return obj
606 return obj
605
607
606 def revoke_user_permission(self, repo_group, user):
608 def revoke_user_permission(self, repo_group, user):
607 """
609 """
608 Revoke permission for user on given repository group
610 Revoke permission for user on given repository group
609
611
610 :param repo_group: Instance of RepoGroup, repositories_group_id,
612 :param repo_group: Instance of RepoGroup, repositories_group_id,
611 or repositories_group name
613 or repositories_group name
612 :param user: Instance of User, user_id or username
614 :param user: Instance of User, user_id or username
613 """
615 """
614
616
615 repo_group = self._get_repo_group(repo_group)
617 repo_group = self._get_repo_group(repo_group)
616 user = self._get_user(user)
618 user = self._get_user(user)
617
619
618 obj = self.sa.query(UserRepoGroupToPerm)\
620 obj = self.sa.query(UserRepoGroupToPerm)\
619 .filter(UserRepoGroupToPerm.user == user)\
621 .filter(UserRepoGroupToPerm.user == user)\
620 .filter(UserRepoGroupToPerm.group == repo_group)\
622 .filter(UserRepoGroupToPerm.group == repo_group)\
621 .scalar()
623 .scalar()
622 if obj:
624 if obj:
623 self.sa.delete(obj)
625 self.sa.delete(obj)
624 log.debug('Revoked perm on %s on %s', repo_group, user)
626 log.debug('Revoked perm on %s on %s', repo_group, user)
625 action_logger_generic(
627 action_logger_generic(
626 'revoked permission from user: {} on repogroup: {}'.format(
628 'revoked permission from user: {} on repogroup: {}'.format(
627 user, repo_group), namespace='security.repogroup')
629 user, repo_group), namespace='security.repogroup')
628
630
629 def grant_user_group_permission(self, repo_group, group_name, perm):
631 def grant_user_group_permission(self, repo_group, group_name, perm):
630 """
632 """
631 Grant permission for user group on given repository group, or update
633 Grant permission for user group on given repository group, or update
632 existing one if found
634 existing one if found
633
635
634 :param repo_group: Instance of RepoGroup, repositories_group_id,
636 :param repo_group: Instance of RepoGroup, repositories_group_id,
635 or repositories_group name
637 or repositories_group name
636 :param group_name: Instance of UserGroup, users_group_id,
638 :param group_name: Instance of UserGroup, users_group_id,
637 or user group name
639 or user group name
638 :param perm: Instance of Permission, or permission_name
640 :param perm: Instance of Permission, or permission_name
639 """
641 """
640 repo_group = self._get_repo_group(repo_group)
642 repo_group = self._get_repo_group(repo_group)
641 group_name = self._get_user_group(group_name)
643 group_name = self._get_user_group(group_name)
642 permission = self._get_perm(perm)
644 permission = self._get_perm(perm)
643
645
644 # check if we have that permission already
646 # check if we have that permission already
645 obj = self.sa.query(UserGroupRepoGroupToPerm)\
647 obj = self.sa.query(UserGroupRepoGroupToPerm)\
646 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
648 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
647 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
649 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
648 .scalar()
650 .scalar()
649
651
650 if obj is None:
652 if obj is None:
651 # create new
653 # create new
652 obj = UserGroupRepoGroupToPerm()
654 obj = UserGroupRepoGroupToPerm()
653
655
654 obj.group = repo_group
656 obj.group = repo_group
655 obj.users_group = group_name
657 obj.users_group = group_name
656 obj.permission = permission
658 obj.permission = permission
657 self.sa.add(obj)
659 self.sa.add(obj)
658 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
660 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
659 action_logger_generic(
661 action_logger_generic(
660 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
662 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
661 perm, group_name, repo_group), namespace='security.repogroup')
663 perm, group_name, repo_group), namespace='security.repogroup')
662 return obj
664 return obj
663
665
664 def revoke_user_group_permission(self, repo_group, group_name):
666 def revoke_user_group_permission(self, repo_group, group_name):
665 """
667 """
666 Revoke permission for user group on given repository group
668 Revoke permission for user group on given repository group
667
669
668 :param repo_group: Instance of RepoGroup, repositories_group_id,
670 :param repo_group: Instance of RepoGroup, repositories_group_id,
669 or repositories_group name
671 or repositories_group name
670 :param group_name: Instance of UserGroup, users_group_id,
672 :param group_name: Instance of UserGroup, users_group_id,
671 or user group name
673 or user group name
672 """
674 """
673 repo_group = self._get_repo_group(repo_group)
675 repo_group = self._get_repo_group(repo_group)
674 group_name = self._get_user_group(group_name)
676 group_name = self._get_user_group(group_name)
675
677
676 obj = self.sa.query(UserGroupRepoGroupToPerm)\
678 obj = self.sa.query(UserGroupRepoGroupToPerm)\
677 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
679 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
678 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
680 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
679 .scalar()
681 .scalar()
680 if obj:
682 if obj:
681 self.sa.delete(obj)
683 self.sa.delete(obj)
682 log.debug('Revoked perm to %s on %s', repo_group, group_name)
684 log.debug('Revoked perm to %s on %s', repo_group, group_name)
683 action_logger_generic(
685 action_logger_generic(
684 'revoked permission from usergroup: {} on repogroup: {}'.format(
686 'revoked permission from usergroup: {} on repogroup: {}'.format(
685 group_name, repo_group), namespace='security.repogroup')
687 group_name, repo_group), namespace='security.repogroup')
686
688
687 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
689 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
688 super_user_actions=False):
690 super_user_actions=False):
689
691
690 from pyramid.threadlocal import get_current_request
692 from pyramid.threadlocal import get_current_request
691 _render = get_current_request().get_partial_renderer(
693 _render = get_current_request().get_partial_renderer(
692 'rhodecode:templates/data_table/_dt_elements.mako')
694 'rhodecode:templates/data_table/_dt_elements.mako')
693 c = _render.get_call_context()
695 c = _render.get_call_context()
694 h = _render.get_helpers()
696 h = _render.get_helpers()
695
697
696 def quick_menu(repo_group_name):
698 def quick_menu(repo_group_name):
697 return _render('quick_repo_group_menu', repo_group_name)
699 return _render('quick_repo_group_menu', repo_group_name)
698
700
699 def repo_group_lnk(repo_group_name):
701 def repo_group_lnk(repo_group_name):
700 return _render('repo_group_name', repo_group_name)
702 return _render('repo_group_name', repo_group_name)
701
703
702 def last_change(last_change):
704 def last_change(last_change):
703 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
705 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
704 last_change = last_change + datetime.timedelta(seconds=
706 last_change = last_change + datetime.timedelta(seconds=
705 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
707 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
706 return _render("last_change", last_change)
708 return _render("last_change", last_change)
707
709
708 def desc(desc, personal):
710 def desc(desc, personal):
709 return _render(
711 return _render(
710 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
712 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
711
713
712 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
714 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
713 return _render(
715 return _render(
714 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
716 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
715
717
716 def repo_group_name(repo_group_name, children_groups):
718 def repo_group_name(repo_group_name, children_groups):
717 return _render("repo_group_name", repo_group_name, children_groups)
719 return _render("repo_group_name", repo_group_name, children_groups)
718
720
719 def user_profile(username):
721 def user_profile(username):
720 return _render('user_profile', username)
722 return _render('user_profile', username)
721
723
722 repo_group_data = []
724 repo_group_data = []
723 for group in repo_group_list:
725 for group in repo_group_list:
724
726
725 row = {
727 row = {
726 "menu": quick_menu(group.group_name),
728 "menu": quick_menu(group.group_name),
727 "name": repo_group_lnk(group.group_name),
729 "name": repo_group_lnk(group.group_name),
728 "name_raw": group.group_name,
730 "name_raw": group.group_name,
729 "last_change": last_change(group.last_db_change),
731 "last_change": last_change(group.last_db_change),
730 "last_change_raw": datetime_to_time(group.last_db_change),
732 "last_change_raw": datetime_to_time(group.last_db_change),
731
733
732 "last_changeset": "",
734 "last_changeset": "",
733 "last_changeset_raw": "",
735 "last_changeset_raw": "",
734
736
735 "desc": desc(group.description_safe, group.personal),
737 "desc": desc(group.description_safe, group.personal),
736 "top_level_repos": 0,
738 "top_level_repos": 0,
737 "owner": user_profile(group.user.username)
739 "owner": user_profile(group.user.username)
738 }
740 }
739 if admin:
741 if admin:
740 repo_count = group.repositories.count()
742 repo_count = group.repositories.count()
741 children_groups = map(
743 children_groups = map(
742 h.safe_unicode,
744 h.safe_unicode,
743 itertools.chain((g.name for g in group.parents),
745 itertools.chain((g.name for g in group.parents),
744 (x.name for x in [group])))
746 (x.name for x in [group])))
745 row.update({
747 row.update({
746 "action": repo_group_actions(
748 "action": repo_group_actions(
747 group.group_id, group.group_name, repo_count),
749 group.group_id, group.group_name, repo_count),
748 "top_level_repos": repo_count,
750 "top_level_repos": repo_count,
749 "name": repo_group_name(group.group_name, children_groups),
751 "name": repo_group_name(group.group_name, children_groups),
750
752
751 })
753 })
752 repo_group_data.append(row)
754 repo_group_data.append(row)
753
755
754 return repo_group_data
756 return repo_group_data
755
757
756 def _get_defaults(self, repo_group_name):
758 def _get_defaults(self, repo_group_name):
757 repo_group = RepoGroup.get_by_group_name(repo_group_name)
759 repo_group = RepoGroup.get_by_group_name(repo_group_name)
758
760
759 if repo_group is None:
761 if repo_group is None:
760 return None
762 return None
761
763
762 defaults = repo_group.get_dict()
764 defaults = repo_group.get_dict()
763 defaults['repo_group_name'] = repo_group.name
765 defaults['repo_group_name'] = repo_group.name
764 defaults['repo_group_description'] = repo_group.group_description
766 defaults['repo_group_description'] = repo_group.group_description
765 defaults['repo_group_enable_locking'] = repo_group.enable_locking
767 defaults['repo_group_enable_locking'] = repo_group.enable_locking
766
768
767 # we use -1 as this is how in HTML, we mark an empty group
769 # we use -1 as this is how in HTML, we mark an empty group
768 defaults['repo_group'] = defaults['group_parent_id'] or -1
770 defaults['repo_group'] = defaults['group_parent_id'] or -1
769
771
770 # fill owner
772 # fill owner
771 if repo_group.user:
773 if repo_group.user:
772 defaults.update({'user': repo_group.user.username})
774 defaults.update({'user': repo_group.user.username})
773 else:
775 else:
774 replacement_user = User.get_first_super_admin().username
776 replacement_user = User.get_first_super_admin().username
775 defaults.update({'user': replacement_user})
777 defaults.update({'user': replacement_user})
776
778
777 return defaults
779 return defaults
General Comments 0
You need to be logged in to leave comments. Login now