##// END OF EJS Templates
audit-logs: added audit-logs on user actions....
marcink -
r1801:c1a16410 default
parent child Browse files
Show More
@@ -1,629 +1,643 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 Users crud controller for pylons
22 Users crud controller for pylons
23 """
23 """
24
24
25 import logging
25 import logging
26 import formencode
26 import formencode
27
27
28 from formencode import htmlfill
28 from formencode import htmlfill
29 from pylons import request, tmpl_context as c, url, config
29 from pylons import request, tmpl_context as c, url, config
30 from pylons.controllers.util import redirect
30 from pylons.controllers.util import redirect
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from rhodecode.authentication.plugins import auth_rhodecode
33 from rhodecode.authentication.plugins import auth_rhodecode
34
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib import auth
37 from rhodecode.lib import audit_logger
38 from rhodecode.lib.auth import (
39 LoginRequired, HasPermissionAllDecorator, AuthUser)
40 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.exceptions import (
41 from rhodecode.lib.exceptions import (
35 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
42 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
36 UserOwnsUserGroupsException, UserCreationError)
43 UserOwnsUserGroupsException, UserCreationError)
37 from rhodecode.lib import helpers as h
44 from rhodecode.lib.utils2 import safe_int, AttributeDict
38 from rhodecode.lib import auth
39 from rhodecode.lib.auth import (
40 LoginRequired, HasPermissionAllDecorator, AuthUser, generate_auth_token)
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.model.auth_token import AuthTokenModel
43
45
44 from rhodecode.model.db import (
46 from rhodecode.model.db import (
45 PullRequestReviewers, User, UserEmailMap, UserIpMap, RepoGroup)
47 PullRequestReviewers, User, UserEmailMap, UserIpMap, RepoGroup)
46 from rhodecode.model.forms import (
48 from rhodecode.model.forms import (
47 UserForm, UserPermissionsForm, UserIndividualPermissionsForm)
49 UserForm, UserPermissionsForm, UserIndividualPermissionsForm)
48 from rhodecode.model.repo_group import RepoGroupModel
50 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
50 from rhodecode.model.meta import Session
52 from rhodecode.model.meta import Session
51 from rhodecode.model.permission import PermissionModel
53 from rhodecode.model.permission import PermissionModel
52 from rhodecode.lib.utils import action_logger
53 from rhodecode.lib.utils2 import datetime_to_time, safe_int, AttributeDict
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class UsersController(BaseController):
58 class UsersController(BaseController):
59 """REST Controller styled on the Atom Publishing Protocol"""
59 """REST Controller styled on the Atom Publishing Protocol"""
60
60
61 @LoginRequired()
61 @LoginRequired()
62 def __before__(self):
62 def __before__(self):
63 super(UsersController, self).__before__()
63 super(UsersController, self).__before__()
64 c.available_permissions = config['available_permissions']
64 c.available_permissions = config['available_permissions']
65 c.allowed_languages = [
65 c.allowed_languages = [
66 ('en', 'English (en)'),
66 ('en', 'English (en)'),
67 ('de', 'German (de)'),
67 ('de', 'German (de)'),
68 ('fr', 'French (fr)'),
68 ('fr', 'French (fr)'),
69 ('it', 'Italian (it)'),
69 ('it', 'Italian (it)'),
70 ('ja', 'Japanese (ja)'),
70 ('ja', 'Japanese (ja)'),
71 ('pl', 'Polish (pl)'),
71 ('pl', 'Polish (pl)'),
72 ('pt', 'Portuguese (pt)'),
72 ('pt', 'Portuguese (pt)'),
73 ('ru', 'Russian (ru)'),
73 ('ru', 'Russian (ru)'),
74 ('zh', 'Chinese (zh)'),
74 ('zh', 'Chinese (zh)'),
75 ]
75 ]
76 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
76 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
77
77
78 def _get_personal_repo_group_template_vars(self):
78 def _get_personal_repo_group_template_vars(self):
79 DummyUser = AttributeDict({
79 DummyUser = AttributeDict({
80 'username': '${username}',
80 'username': '${username}',
81 'user_id': '${user_id}',
81 'user_id': '${user_id}',
82 })
82 })
83 c.default_create_repo_group = RepoGroupModel() \
83 c.default_create_repo_group = RepoGroupModel() \
84 .get_default_create_personal_repo_group()
84 .get_default_create_personal_repo_group()
85 c.personal_repo_group_name = RepoGroupModel() \
85 c.personal_repo_group_name = RepoGroupModel() \
86 .get_personal_group_name(DummyUser)
86 .get_personal_group_name(DummyUser)
87
87
88 @HasPermissionAllDecorator('hg.admin')
88 @HasPermissionAllDecorator('hg.admin')
89 @auth.CSRFRequired()
89 @auth.CSRFRequired()
90 def create(self):
90 def create(self):
91 """POST /users: Create a new item"""
92 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
91 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
93 user_model = UserModel()
92 user_model = UserModel()
94 user_form = UserForm()()
93 user_form = UserForm()()
95 try:
94 try:
96 form_result = user_form.to_python(dict(request.POST))
95 form_result = user_form.to_python(dict(request.POST))
97 user = user_model.create(form_result)
96 user = user_model.create(form_result)
98 Session().flush()
97 Session().flush()
98 creation_data = user.get_api_data()
99 username = form_result['username']
99 username = form_result['username']
100 action_logger(c.rhodecode_user, 'admin_created_user:%s' % username,
100
101 None, self.ip_addr, self.sa)
101 audit_logger.store_web(
102 'user.create', action_data={'data': creation_data},
103 user=c.rhodecode_user)
102
104
103 user_link = h.link_to(h.escape(username),
105 user_link = h.link_to(h.escape(username),
104 url('edit_user',
106 url('edit_user',
105 user_id=user.user_id))
107 user_id=user.user_id))
106 h.flash(h.literal(_('Created user %(user_link)s')
108 h.flash(h.literal(_('Created user %(user_link)s')
107 % {'user_link': user_link}), category='success')
109 % {'user_link': user_link}), category='success')
108 Session().commit()
110 Session().commit()
109 except formencode.Invalid as errors:
111 except formencode.Invalid as errors:
110 self._get_personal_repo_group_template_vars()
112 self._get_personal_repo_group_template_vars()
111 return htmlfill.render(
113 return htmlfill.render(
112 render('admin/users/user_add.mako'),
114 render('admin/users/user_add.mako'),
113 defaults=errors.value,
115 defaults=errors.value,
114 errors=errors.error_dict or {},
116 errors=errors.error_dict or {},
115 prefix_error=False,
117 prefix_error=False,
116 encoding="UTF-8",
118 encoding="UTF-8",
117 force_defaults=False)
119 force_defaults=False)
118 except UserCreationError as e:
120 except UserCreationError as e:
119 h.flash(e, 'error')
121 h.flash(e, 'error')
120 except Exception:
122 except Exception:
121 log.exception("Exception creation of user")
123 log.exception("Exception creation of user")
122 h.flash(_('Error occurred during creation of user %s')
124 h.flash(_('Error occurred during creation of user %s')
123 % request.POST.get('username'), category='error')
125 % request.POST.get('username'), category='error')
124 return redirect(h.route_path('users'))
126 return redirect(h.route_path('users'))
125
127
126 @HasPermissionAllDecorator('hg.admin')
128 @HasPermissionAllDecorator('hg.admin')
127 def new(self):
129 def new(self):
128 """GET /users/new: Form to create a new item"""
129 # url('new_user')
130 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
130 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
131 self._get_personal_repo_group_template_vars()
131 self._get_personal_repo_group_template_vars()
132 return render('admin/users/user_add.mako')
132 return render('admin/users/user_add.mako')
133
133
134 @HasPermissionAllDecorator('hg.admin')
134 @HasPermissionAllDecorator('hg.admin')
135 @auth.CSRFRequired()
135 @auth.CSRFRequired()
136 def update(self, user_id):
136 def update(self, user_id):
137 """PUT /users/user_id: Update an existing item"""
137
138 # Forms posted to this method should contain a hidden field:
139 # <input type="hidden" name="_method" value="PUT" />
140 # Or using helpers:
141 # h.form(url('update_user', user_id=ID),
142 # method='put')
143 # url('user', user_id=ID)
144 user_id = safe_int(user_id)
138 user_id = safe_int(user_id)
145 c.user = User.get_or_404(user_id)
139 c.user = User.get_or_404(user_id)
146 c.active = 'profile'
140 c.active = 'profile'
147 c.extern_type = c.user.extern_type
141 c.extern_type = c.user.extern_type
148 c.extern_name = c.user.extern_name
142 c.extern_name = c.user.extern_name
149 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
143 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
150 available_languages = [x[0] for x in c.allowed_languages]
144 available_languages = [x[0] for x in c.allowed_languages]
151 _form = UserForm(edit=True, available_languages=available_languages,
145 _form = UserForm(edit=True, available_languages=available_languages,
152 old_data={'user_id': user_id,
146 old_data={'user_id': user_id,
153 'email': c.user.email})()
147 'email': c.user.email})()
154 form_result = {}
148 form_result = {}
149 old_values = c.user.get_api_data()
155 try:
150 try:
156 form_result = _form.to_python(dict(request.POST))
151 form_result = _form.to_python(dict(request.POST))
157 skip_attrs = ['extern_type', 'extern_name']
152 skip_attrs = ['extern_type', 'extern_name']
158 # TODO: plugin should define if username can be updated
153 # TODO: plugin should define if username can be updated
159 if c.extern_type != "rhodecode":
154 if c.extern_type != "rhodecode":
160 # forbid updating username for external accounts
155 # forbid updating username for external accounts
161 skip_attrs.append('username')
156 skip_attrs.append('username')
162
157
163 UserModel().update_user(user_id, skip_attrs=skip_attrs, **form_result)
158 UserModel().update_user(
164 usr = form_result['username']
159 user_id, skip_attrs=skip_attrs, **form_result)
165 action_logger(c.rhodecode_user, 'admin_updated_user:%s' % usr,
160
166 None, self.ip_addr, self.sa)
161 audit_logger.store_web(
162 'user.edit', action_data={'old_data': old_values},
163 user=c.rhodecode_user)
164
165 Session().commit()
167 h.flash(_('User updated successfully'), category='success')
166 h.flash(_('User updated successfully'), category='success')
168 Session().commit()
169 except formencode.Invalid as errors:
167 except formencode.Invalid as errors:
170 defaults = errors.value
168 defaults = errors.value
171 e = errors.error_dict or {}
169 e = errors.error_dict or {}
172
170
173 return htmlfill.render(
171 return htmlfill.render(
174 render('admin/users/user_edit.mako'),
172 render('admin/users/user_edit.mako'),
175 defaults=defaults,
173 defaults=defaults,
176 errors=e,
174 errors=e,
177 prefix_error=False,
175 prefix_error=False,
178 encoding="UTF-8",
176 encoding="UTF-8",
179 force_defaults=False)
177 force_defaults=False)
180 except UserCreationError as e:
178 except UserCreationError as e:
181 h.flash(e, 'error')
179 h.flash(e, 'error')
182 except Exception:
180 except Exception:
183 log.exception("Exception updating user")
181 log.exception("Exception updating user")
184 h.flash(_('Error occurred during update of user %s')
182 h.flash(_('Error occurred during update of user %s')
185 % form_result.get('username'), category='error')
183 % form_result.get('username'), category='error')
186 return redirect(url('edit_user', user_id=user_id))
184 return redirect(url('edit_user', user_id=user_id))
187
185
188 @HasPermissionAllDecorator('hg.admin')
186 @HasPermissionAllDecorator('hg.admin')
189 @auth.CSRFRequired()
187 @auth.CSRFRequired()
190 def delete(self, user_id):
188 def delete(self, user_id):
191 """DELETE /users/user_id: Delete an existing item"""
192 # Forms posted to this method should contain a hidden field:
193 # <input type="hidden" name="_method" value="DELETE" />
194 # Or using helpers:
195 # h.form(url('delete_user', user_id=ID),
196 # method='delete')
197 # url('user', user_id=ID)
198 user_id = safe_int(user_id)
189 user_id = safe_int(user_id)
199 c.user = User.get_or_404(user_id)
190 c.user = User.get_or_404(user_id)
200
191
201 _repos = c.user.repositories
192 _repos = c.user.repositories
202 _repo_groups = c.user.repository_groups
193 _repo_groups = c.user.repository_groups
203 _user_groups = c.user.user_groups
194 _user_groups = c.user.user_groups
204
195
205 handle_repos = None
196 handle_repos = None
206 handle_repo_groups = None
197 handle_repo_groups = None
207 handle_user_groups = None
198 handle_user_groups = None
208 # dummy call for flash of handle
199 # dummy call for flash of handle
209 set_handle_flash_repos = lambda: None
200 set_handle_flash_repos = lambda: None
210 set_handle_flash_repo_groups = lambda: None
201 set_handle_flash_repo_groups = lambda: None
211 set_handle_flash_user_groups = lambda: None
202 set_handle_flash_user_groups = lambda: None
212
203
213 if _repos and request.POST.get('user_repos'):
204 if _repos and request.POST.get('user_repos'):
214 do = request.POST['user_repos']
205 do = request.POST['user_repos']
215 if do == 'detach':
206 if do == 'detach':
216 handle_repos = 'detach'
207 handle_repos = 'detach'
217 set_handle_flash_repos = lambda: h.flash(
208 set_handle_flash_repos = lambda: h.flash(
218 _('Detached %s repositories') % len(_repos),
209 _('Detached %s repositories') % len(_repos),
219 category='success')
210 category='success')
220 elif do == 'delete':
211 elif do == 'delete':
221 handle_repos = 'delete'
212 handle_repos = 'delete'
222 set_handle_flash_repos = lambda: h.flash(
213 set_handle_flash_repos = lambda: h.flash(
223 _('Deleted %s repositories') % len(_repos),
214 _('Deleted %s repositories') % len(_repos),
224 category='success')
215 category='success')
225
216
226 if _repo_groups and request.POST.get('user_repo_groups'):
217 if _repo_groups and request.POST.get('user_repo_groups'):
227 do = request.POST['user_repo_groups']
218 do = request.POST['user_repo_groups']
228 if do == 'detach':
219 if do == 'detach':
229 handle_repo_groups = 'detach'
220 handle_repo_groups = 'detach'
230 set_handle_flash_repo_groups = lambda: h.flash(
221 set_handle_flash_repo_groups = lambda: h.flash(
231 _('Detached %s repository groups') % len(_repo_groups),
222 _('Detached %s repository groups') % len(_repo_groups),
232 category='success')
223 category='success')
233 elif do == 'delete':
224 elif do == 'delete':
234 handle_repo_groups = 'delete'
225 handle_repo_groups = 'delete'
235 set_handle_flash_repo_groups = lambda: h.flash(
226 set_handle_flash_repo_groups = lambda: h.flash(
236 _('Deleted %s repository groups') % len(_repo_groups),
227 _('Deleted %s repository groups') % len(_repo_groups),
237 category='success')
228 category='success')
238
229
239 if _user_groups and request.POST.get('user_user_groups'):
230 if _user_groups and request.POST.get('user_user_groups'):
240 do = request.POST['user_user_groups']
231 do = request.POST['user_user_groups']
241 if do == 'detach':
232 if do == 'detach':
242 handle_user_groups = 'detach'
233 handle_user_groups = 'detach'
243 set_handle_flash_user_groups = lambda: h.flash(
234 set_handle_flash_user_groups = lambda: h.flash(
244 _('Detached %s user groups') % len(_user_groups),
235 _('Detached %s user groups') % len(_user_groups),
245 category='success')
236 category='success')
246 elif do == 'delete':
237 elif do == 'delete':
247 handle_user_groups = 'delete'
238 handle_user_groups = 'delete'
248 set_handle_flash_user_groups = lambda: h.flash(
239 set_handle_flash_user_groups = lambda: h.flash(
249 _('Deleted %s user groups') % len(_user_groups),
240 _('Deleted %s user groups') % len(_user_groups),
250 category='success')
241 category='success')
251
242
243 old_values = c.user.get_api_data()
252 try:
244 try:
253 UserModel().delete(c.user, handle_repos=handle_repos,
245 UserModel().delete(c.user, handle_repos=handle_repos,
254 handle_repo_groups=handle_repo_groups,
246 handle_repo_groups=handle_repo_groups,
255 handle_user_groups=handle_user_groups)
247 handle_user_groups=handle_user_groups)
248
249 audit_logger.store_web(
250 'user.delete', action_data={'old_data': old_values},
251 user=c.rhodecode_user)
252
256 Session().commit()
253 Session().commit()
257 set_handle_flash_repos()
254 set_handle_flash_repos()
258 set_handle_flash_repo_groups()
255 set_handle_flash_repo_groups()
259 set_handle_flash_user_groups()
256 set_handle_flash_user_groups()
260 h.flash(_('Successfully deleted user'), category='success')
257 h.flash(_('Successfully deleted user'), category='success')
261 except (UserOwnsReposException, UserOwnsRepoGroupsException,
258 except (UserOwnsReposException, UserOwnsRepoGroupsException,
262 UserOwnsUserGroupsException, DefaultUserException) as e:
259 UserOwnsUserGroupsException, DefaultUserException) as e:
263 h.flash(e, category='warning')
260 h.flash(e, category='warning')
264 except Exception:
261 except Exception:
265 log.exception("Exception during deletion of user")
262 log.exception("Exception during deletion of user")
266 h.flash(_('An error occurred during deletion of user'),
263 h.flash(_('An error occurred during deletion of user'),
267 category='error')
264 category='error')
268 return redirect(h.route_path('users'))
265 return redirect(h.route_path('users'))
269
266
270 @HasPermissionAllDecorator('hg.admin')
267 @HasPermissionAllDecorator('hg.admin')
271 @auth.CSRFRequired()
268 @auth.CSRFRequired()
272 def reset_password(self, user_id):
269 def reset_password(self, user_id):
273 """
270 """
274 toggle reset password flag for this user
271 toggle reset password flag for this user
275
276 :param user_id:
277 """
272 """
278 user_id = safe_int(user_id)
273 user_id = safe_int(user_id)
279 c.user = User.get_or_404(user_id)
274 c.user = User.get_or_404(user_id)
280 try:
275 try:
281 old_value = c.user.user_data.get('force_password_change')
276 old_value = c.user.user_data.get('force_password_change')
282 c.user.update_userdata(force_password_change=not old_value)
277 c.user.update_userdata(force_password_change=not old_value)
283 Session().commit()
278
284 if old_value:
279 if old_value:
285 msg = _('Force password change disabled for user')
280 msg = _('Force password change disabled for user')
281 audit_logger.store_web(
282 'user.edit.password_reset.disabled',
283 user=c.rhodecode_user)
286 else:
284 else:
287 msg = _('Force password change enabled for user')
285 msg = _('Force password change enabled for user')
286 audit_logger.store_web(
287 'user.edit.password_reset.enabled',
288 user=c.rhodecode_user)
289
290 Session().commit()
288 h.flash(msg, category='success')
291 h.flash(msg, category='success')
289 except Exception:
292 except Exception:
290 log.exception("Exception during password reset for user")
293 log.exception("Exception during password reset for user")
291 h.flash(_('An error occurred during password reset for user'),
294 h.flash(_('An error occurred during password reset for user'),
292 category='error')
295 category='error')
293
296
294 return redirect(url('edit_user_advanced', user_id=user_id))
297 return redirect(url('edit_user_advanced', user_id=user_id))
295
298
296 @HasPermissionAllDecorator('hg.admin')
299 @HasPermissionAllDecorator('hg.admin')
297 @auth.CSRFRequired()
300 @auth.CSRFRequired()
298 def create_personal_repo_group(self, user_id):
301 def create_personal_repo_group(self, user_id):
299 """
302 """
300 Create personal repository group for this user
303 Create personal repository group for this user
301
302 :param user_id:
303 """
304 """
304 from rhodecode.model.repo_group import RepoGroupModel
305 from rhodecode.model.repo_group import RepoGroupModel
305
306
306 user_id = safe_int(user_id)
307 user_id = safe_int(user_id)
307 c.user = User.get_or_404(user_id)
308 c.user = User.get_or_404(user_id)
308 personal_repo_group = RepoGroup.get_user_personal_repo_group(
309 personal_repo_group = RepoGroup.get_user_personal_repo_group(
309 c.user.user_id)
310 c.user.user_id)
310 if personal_repo_group:
311 if personal_repo_group:
311 return redirect(url('edit_user_advanced', user_id=user_id))
312 return redirect(url('edit_user_advanced', user_id=user_id))
312
313
313 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
314 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
314 c.user)
315 c.user)
315 named_personal_group = RepoGroup.get_by_group_name(
316 named_personal_group = RepoGroup.get_by_group_name(
316 personal_repo_group_name)
317 personal_repo_group_name)
317 try:
318 try:
318
319
319 if named_personal_group and named_personal_group.user_id == c.user.user_id:
320 if named_personal_group and named_personal_group.user_id == c.user.user_id:
320 # migrate the same named group, and mark it as personal
321 # migrate the same named group, and mark it as personal
321 named_personal_group.personal = True
322 named_personal_group.personal = True
322 Session().add(named_personal_group)
323 Session().add(named_personal_group)
323 Session().commit()
324 Session().commit()
324 msg = _('Linked repository group `%s` as personal' % (
325 msg = _('Linked repository group `%s` as personal' % (
325 personal_repo_group_name,))
326 personal_repo_group_name,))
326 h.flash(msg, category='success')
327 h.flash(msg, category='success')
327 elif not named_personal_group:
328 elif not named_personal_group:
328 RepoGroupModel().create_personal_repo_group(c.user)
329 RepoGroupModel().create_personal_repo_group(c.user)
329
330
330 msg = _('Created repository group `%s`' % (
331 msg = _('Created repository group `%s`' % (
331 personal_repo_group_name,))
332 personal_repo_group_name,))
332 h.flash(msg, category='success')
333 h.flash(msg, category='success')
333 else:
334 else:
334 msg = _('Repository group `%s` is already taken' % (
335 msg = _('Repository group `%s` is already taken' % (
335 personal_repo_group_name,))
336 personal_repo_group_name,))
336 h.flash(msg, category='warning')
337 h.flash(msg, category='warning')
337 except Exception:
338 except Exception:
338 log.exception("Exception during repository group creation")
339 log.exception("Exception during repository group creation")
339 msg = _(
340 msg = _(
340 'An error occurred during repository group creation for user')
341 'An error occurred during repository group creation for user')
341 h.flash(msg, category='error')
342 h.flash(msg, category='error')
342 Session().rollback()
343 Session().rollback()
343
344
344 return redirect(url('edit_user_advanced', user_id=user_id))
345 return redirect(url('edit_user_advanced', user_id=user_id))
345
346
346 @HasPermissionAllDecorator('hg.admin')
347 @HasPermissionAllDecorator('hg.admin')
347 def show(self, user_id):
348 def show(self, user_id):
348 """GET /users/user_id: Show a specific item"""
349 """GET /users/user_id: Show a specific item"""
349 # url('user', user_id=ID)
350 # url('user', user_id=ID)
350 User.get_or_404(-1)
351 User.get_or_404(-1)
351
352
352 @HasPermissionAllDecorator('hg.admin')
353 @HasPermissionAllDecorator('hg.admin')
353 def edit(self, user_id):
354 def edit(self, user_id):
354 """GET /users/user_id/edit: Form to edit an existing item"""
355 """GET /users/user_id/edit: Form to edit an existing item"""
355 # url('edit_user', user_id=ID)
356 # url('edit_user', user_id=ID)
356 user_id = safe_int(user_id)
357 user_id = safe_int(user_id)
357 c.user = User.get_or_404(user_id)
358 c.user = User.get_or_404(user_id)
358 if c.user.username == User.DEFAULT_USER:
359 if c.user.username == User.DEFAULT_USER:
359 h.flash(_("You can't edit this user"), category='warning')
360 h.flash(_("You can't edit this user"), category='warning')
360 return redirect(h.route_path('users'))
361 return redirect(h.route_path('users'))
361
362
362 c.active = 'profile'
363 c.active = 'profile'
363 c.extern_type = c.user.extern_type
364 c.extern_type = c.user.extern_type
364 c.extern_name = c.user.extern_name
365 c.extern_name = c.user.extern_name
365 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
366 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
366
367
367 defaults = c.user.get_dict()
368 defaults = c.user.get_dict()
368 defaults.update({'language': c.user.user_data.get('language')})
369 defaults.update({'language': c.user.user_data.get('language')})
369 return htmlfill.render(
370 return htmlfill.render(
370 render('admin/users/user_edit.mako'),
371 render('admin/users/user_edit.mako'),
371 defaults=defaults,
372 defaults=defaults,
372 encoding="UTF-8",
373 encoding="UTF-8",
373 force_defaults=False)
374 force_defaults=False)
374
375
375 @HasPermissionAllDecorator('hg.admin')
376 @HasPermissionAllDecorator('hg.admin')
376 def edit_advanced(self, user_id):
377 def edit_advanced(self, user_id):
377 user_id = safe_int(user_id)
378 user_id = safe_int(user_id)
378 user = c.user = User.get_or_404(user_id)
379 user = c.user = User.get_or_404(user_id)
379 if user.username == User.DEFAULT_USER:
380 if user.username == User.DEFAULT_USER:
380 h.flash(_("You can't edit this user"), category='warning')
381 h.flash(_("You can't edit this user"), category='warning')
381 return redirect(h.route_path('users'))
382 return redirect(h.route_path('users'))
382
383
383 c.active = 'advanced'
384 c.active = 'advanced'
384 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
385 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
385 c.personal_repo_group_name = RepoGroupModel()\
386 c.personal_repo_group_name = RepoGroupModel()\
386 .get_personal_group_name(user)
387 .get_personal_group_name(user)
387 c.first_admin = User.get_first_super_admin()
388 c.first_admin = User.get_first_super_admin()
388 defaults = user.get_dict()
389 defaults = user.get_dict()
389
390
390 # Interim workaround if the user participated on any pull requests as a
391 # Interim workaround if the user participated on any pull requests as a
391 # reviewer.
392 # reviewer.
392 has_review = bool(PullRequestReviewers.query().filter(
393 has_review = bool(PullRequestReviewers.query().filter(
393 PullRequestReviewers.user_id == user_id).first())
394 PullRequestReviewers.user_id == user_id).first())
394 c.can_delete_user = not has_review
395 c.can_delete_user = not has_review
395 c.can_delete_user_message = _(
396 c.can_delete_user_message = _(
396 'The user participates as reviewer in pull requests and '
397 'The user participates as reviewer in pull requests and '
397 'cannot be deleted. You can set the user to '
398 'cannot be deleted. You can set the user to '
398 '"inactive" instead of deleting it.') if has_review else ''
399 '"inactive" instead of deleting it.') if has_review else ''
399
400
400 return htmlfill.render(
401 return htmlfill.render(
401 render('admin/users/user_edit.mako'),
402 render('admin/users/user_edit.mako'),
402 defaults=defaults,
403 defaults=defaults,
403 encoding="UTF-8",
404 encoding="UTF-8",
404 force_defaults=False)
405 force_defaults=False)
405
406
406 @HasPermissionAllDecorator('hg.admin')
407 @HasPermissionAllDecorator('hg.admin')
407 def edit_global_perms(self, user_id):
408 def edit_global_perms(self, user_id):
408 user_id = safe_int(user_id)
409 user_id = safe_int(user_id)
409 c.user = User.get_or_404(user_id)
410 c.user = User.get_or_404(user_id)
410 if c.user.username == User.DEFAULT_USER:
411 if c.user.username == User.DEFAULT_USER:
411 h.flash(_("You can't edit this user"), category='warning')
412 h.flash(_("You can't edit this user"), category='warning')
412 return redirect(h.route_path('users'))
413 return redirect(h.route_path('users'))
413
414
414 c.active = 'global_perms'
415 c.active = 'global_perms'
415
416
416 c.default_user = User.get_default_user()
417 c.default_user = User.get_default_user()
417 defaults = c.user.get_dict()
418 defaults = c.user.get_dict()
418 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
419 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
419 defaults.update(c.default_user.get_default_perms())
420 defaults.update(c.default_user.get_default_perms())
420 defaults.update(c.user.get_default_perms())
421 defaults.update(c.user.get_default_perms())
421
422
422 return htmlfill.render(
423 return htmlfill.render(
423 render('admin/users/user_edit.mako'),
424 render('admin/users/user_edit.mako'),
424 defaults=defaults,
425 defaults=defaults,
425 encoding="UTF-8",
426 encoding="UTF-8",
426 force_defaults=False)
427 force_defaults=False)
427
428
428 @HasPermissionAllDecorator('hg.admin')
429 @HasPermissionAllDecorator('hg.admin')
429 @auth.CSRFRequired()
430 @auth.CSRFRequired()
430 def update_global_perms(self, user_id):
431 def update_global_perms(self, user_id):
431 """PUT /users_perm/user_id: Update an existing item"""
432 # url('user_perm', user_id=ID, method='put')
433 user_id = safe_int(user_id)
432 user_id = safe_int(user_id)
434 user = User.get_or_404(user_id)
433 user = User.get_or_404(user_id)
435 c.active = 'global_perms'
434 c.active = 'global_perms'
436 try:
435 try:
437 # first stage that verifies the checkbox
436 # first stage that verifies the checkbox
438 _form = UserIndividualPermissionsForm()
437 _form = UserIndividualPermissionsForm()
439 form_result = _form.to_python(dict(request.POST))
438 form_result = _form.to_python(dict(request.POST))
440 inherit_perms = form_result['inherit_default_permissions']
439 inherit_perms = form_result['inherit_default_permissions']
441 user.inherit_default_permissions = inherit_perms
440 user.inherit_default_permissions = inherit_perms
442 Session().add(user)
441 Session().add(user)
443
442
444 if not inherit_perms:
443 if not inherit_perms:
445 # only update the individual ones if we un check the flag
444 # only update the individual ones if we un check the flag
446 _form = UserPermissionsForm(
445 _form = UserPermissionsForm(
447 [x[0] for x in c.repo_create_choices],
446 [x[0] for x in c.repo_create_choices],
448 [x[0] for x in c.repo_create_on_write_choices],
447 [x[0] for x in c.repo_create_on_write_choices],
449 [x[0] for x in c.repo_group_create_choices],
448 [x[0] for x in c.repo_group_create_choices],
450 [x[0] for x in c.user_group_create_choices],
449 [x[0] for x in c.user_group_create_choices],
451 [x[0] for x in c.fork_choices],
450 [x[0] for x in c.fork_choices],
452 [x[0] for x in c.inherit_default_permission_choices])()
451 [x[0] for x in c.inherit_default_permission_choices])()
453
452
454 form_result = _form.to_python(dict(request.POST))
453 form_result = _form.to_python(dict(request.POST))
455 form_result.update({'perm_user_id': user.user_id})
454 form_result.update({'perm_user_id': user.user_id})
456
455
457 PermissionModel().update_user_permissions(form_result)
456 PermissionModel().update_user_permissions(form_result)
458
457
458 # TODO(marcink): implement global permissions
459 # audit_log.store_web('user.edit.permissions')
460
459 Session().commit()
461 Session().commit()
460 h.flash(_('User global permissions updated successfully'),
462 h.flash(_('User global permissions updated successfully'),
461 category='success')
463 category='success')
462
464
463 Session().commit()
464 except formencode.Invalid as errors:
465 except formencode.Invalid as errors:
465 defaults = errors.value
466 defaults = errors.value
466 c.user = user
467 c.user = user
467 return htmlfill.render(
468 return htmlfill.render(
468 render('admin/users/user_edit.mako'),
469 render('admin/users/user_edit.mako'),
469 defaults=defaults,
470 defaults=defaults,
470 errors=errors.error_dict or {},
471 errors=errors.error_dict or {},
471 prefix_error=False,
472 prefix_error=False,
472 encoding="UTF-8",
473 encoding="UTF-8",
473 force_defaults=False)
474 force_defaults=False)
474 except Exception:
475 except Exception:
475 log.exception("Exception during permissions saving")
476 log.exception("Exception during permissions saving")
476 h.flash(_('An error occurred during permissions saving'),
477 h.flash(_('An error occurred during permissions saving'),
477 category='error')
478 category='error')
478 return redirect(url('edit_user_global_perms', user_id=user_id))
479 return redirect(url('edit_user_global_perms', user_id=user_id))
479
480
480 @HasPermissionAllDecorator('hg.admin')
481 @HasPermissionAllDecorator('hg.admin')
481 def edit_perms_summary(self, user_id):
482 def edit_perms_summary(self, user_id):
482 user_id = safe_int(user_id)
483 user_id = safe_int(user_id)
483 c.user = User.get_or_404(user_id)
484 c.user = User.get_or_404(user_id)
484 if c.user.username == User.DEFAULT_USER:
485 if c.user.username == User.DEFAULT_USER:
485 h.flash(_("You can't edit this user"), category='warning')
486 h.flash(_("You can't edit this user"), category='warning')
486 return redirect(h.route_path('users'))
487 return redirect(h.route_path('users'))
487
488
488 c.active = 'perms_summary'
489 c.active = 'perms_summary'
489 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
490 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
490
491
491 return render('admin/users/user_edit.mako')
492 return render('admin/users/user_edit.mako')
492
493
493 @HasPermissionAllDecorator('hg.admin')
494 @HasPermissionAllDecorator('hg.admin')
494 def edit_emails(self, user_id):
495 def edit_emails(self, user_id):
495 user_id = safe_int(user_id)
496 user_id = safe_int(user_id)
496 c.user = User.get_or_404(user_id)
497 c.user = User.get_or_404(user_id)
497 if c.user.username == User.DEFAULT_USER:
498 if c.user.username == User.DEFAULT_USER:
498 h.flash(_("You can't edit this user"), category='warning')
499 h.flash(_("You can't edit this user"), category='warning')
499 return redirect(h.route_path('users'))
500 return redirect(h.route_path('users'))
500
501
501 c.active = 'emails'
502 c.active = 'emails'
502 c.user_email_map = UserEmailMap.query() \
503 c.user_email_map = UserEmailMap.query() \
503 .filter(UserEmailMap.user == c.user).all()
504 .filter(UserEmailMap.user == c.user).all()
504
505
505 defaults = c.user.get_dict()
506 defaults = c.user.get_dict()
506 return htmlfill.render(
507 return htmlfill.render(
507 render('admin/users/user_edit.mako'),
508 render('admin/users/user_edit.mako'),
508 defaults=defaults,
509 defaults=defaults,
509 encoding="UTF-8",
510 encoding="UTF-8",
510 force_defaults=False)
511 force_defaults=False)
511
512
512 @HasPermissionAllDecorator('hg.admin')
513 @HasPermissionAllDecorator('hg.admin')
513 @auth.CSRFRequired()
514 @auth.CSRFRequired()
514 def add_email(self, user_id):
515 def add_email(self, user_id):
515 """POST /user_emails:Add an existing item"""
516 # url('user_emails', user_id=ID, method='put')
517 user_id = safe_int(user_id)
516 user_id = safe_int(user_id)
518 c.user = User.get_or_404(user_id)
517 c.user = User.get_or_404(user_id)
519
518
520 email = request.POST.get('new_email')
519 email = request.POST.get('new_email')
521 user_model = UserModel()
520 user_model = UserModel()
522
521 user_data = c.user.get_api_data()
523 try:
522 try:
524 user_model.add_extra_email(user_id, email)
523 user_model.add_extra_email(user_id, email)
524 audit_logger.store_web(
525 'user.edit.email.add',
526 action_data={'email': email, 'user': user_data},
527 user=c.rhodecode_user)
525 Session().commit()
528 Session().commit()
526 h.flash(_("Added new email address `%s` for user account") % email,
529 h.flash(_("Added new email address `%s` for user account") % email,
527 category='success')
530 category='success')
528 except formencode.Invalid as error:
531 except formencode.Invalid as error:
529 msg = error.error_dict['email']
532 msg = error.error_dict['email']
530 h.flash(msg, category='error')
533 h.flash(msg, category='error')
531 except Exception:
534 except Exception:
532 log.exception("Exception during email saving")
535 log.exception("Exception during email saving")
533 h.flash(_('An error occurred during email saving'),
536 h.flash(_('An error occurred during email saving'),
534 category='error')
537 category='error')
535 return redirect(url('edit_user_emails', user_id=user_id))
538 return redirect(url('edit_user_emails', user_id=user_id))
536
539
537 @HasPermissionAllDecorator('hg.admin')
540 @HasPermissionAllDecorator('hg.admin')
538 @auth.CSRFRequired()
541 @auth.CSRFRequired()
539 def delete_email(self, user_id):
542 def delete_email(self, user_id):
540 """DELETE /user_emails_delete/user_id: Delete an existing item"""
541 # url('user_emails_delete', user_id=ID, method='delete')
542 user_id = safe_int(user_id)
543 user_id = safe_int(user_id)
543 c.user = User.get_or_404(user_id)
544 c.user = User.get_or_404(user_id)
544 email_id = request.POST.get('del_email_id')
545 email_id = request.POST.get('del_email_id')
545 user_model = UserModel()
546 user_model = UserModel()
547
548 email = UserEmailMap.query().get(email_id).email
549 user_data = c.user.get_api_data()
546 user_model.delete_extra_email(user_id, email_id)
550 user_model.delete_extra_email(user_id, email_id)
551 audit_logger.store_web(
552 'user.edit.email.delete',
553 action_data={'email': email, 'user': user_data},
554 user=c.rhodecode_user)
547 Session().commit()
555 Session().commit()
548 h.flash(_("Removed email address from user account"), category='success')
556 h.flash(_("Removed email address from user account"), category='success')
549 return redirect(url('edit_user_emails', user_id=user_id))
557 return redirect(url('edit_user_emails', user_id=user_id))
550
558
551 @HasPermissionAllDecorator('hg.admin')
559 @HasPermissionAllDecorator('hg.admin')
552 def edit_ips(self, user_id):
560 def edit_ips(self, user_id):
553 user_id = safe_int(user_id)
561 user_id = safe_int(user_id)
554 c.user = User.get_or_404(user_id)
562 c.user = User.get_or_404(user_id)
555 if c.user.username == User.DEFAULT_USER:
563 if c.user.username == User.DEFAULT_USER:
556 h.flash(_("You can't edit this user"), category='warning')
564 h.flash(_("You can't edit this user"), category='warning')
557 return redirect(h.route_path('users'))
565 return redirect(h.route_path('users'))
558
566
559 c.active = 'ips'
567 c.active = 'ips'
560 c.user_ip_map = UserIpMap.query() \
568 c.user_ip_map = UserIpMap.query() \
561 .filter(UserIpMap.user == c.user).all()
569 .filter(UserIpMap.user == c.user).all()
562
570
563 c.inherit_default_ips = c.user.inherit_default_permissions
571 c.inherit_default_ips = c.user.inherit_default_permissions
564 c.default_user_ip_map = UserIpMap.query() \
572 c.default_user_ip_map = UserIpMap.query() \
565 .filter(UserIpMap.user == User.get_default_user()).all()
573 .filter(UserIpMap.user == User.get_default_user()).all()
566
574
567 defaults = c.user.get_dict()
575 defaults = c.user.get_dict()
568 return htmlfill.render(
576 return htmlfill.render(
569 render('admin/users/user_edit.mako'),
577 render('admin/users/user_edit.mako'),
570 defaults=defaults,
578 defaults=defaults,
571 encoding="UTF-8",
579 encoding="UTF-8",
572 force_defaults=False)
580 force_defaults=False)
573
581
574 @HasPermissionAllDecorator('hg.admin')
582 @HasPermissionAllDecorator('hg.admin')
575 @auth.CSRFRequired()
583 @auth.CSRFRequired()
576 def add_ip(self, user_id):
584 def add_ip(self, user_id):
577 """POST /user_ips:Add an existing item"""
578 # url('user_ips', user_id=ID, method='put')
579
580 user_id = safe_int(user_id)
585 user_id = safe_int(user_id)
581 c.user = User.get_or_404(user_id)
586 c.user = User.get_or_404(user_id)
582 user_model = UserModel()
587 user_model = UserModel()
583 try:
588 try:
584 ip_list = user_model.parse_ip_range(request.POST.get('new_ip'))
589 ip_list = user_model.parse_ip_range(request.POST.get('new_ip'))
585 except Exception as e:
590 except Exception as e:
586 ip_list = []
591 ip_list = []
587 log.exception("Exception during ip saving")
592 log.exception("Exception during ip saving")
588 h.flash(_('An error occurred during ip saving:%s' % (e,)),
593 h.flash(_('An error occurred during ip saving:%s' % (e,)),
589 category='error')
594 category='error')
590
595
591 desc = request.POST.get('description')
596 desc = request.POST.get('description')
592 added = []
597 added = []
598 user_data = c.user.get_api_data()
593 for ip in ip_list:
599 for ip in ip_list:
594 try:
600 try:
595 user_model.add_extra_ip(user_id, ip, desc)
601 user_model.add_extra_ip(user_id, ip, desc)
602 audit_logger.store_web(
603 'user.edit.ip.add',
604 action_data={'ip': ip, 'user': user_data},
605 user=c.rhodecode_user)
596 Session().commit()
606 Session().commit()
597 added.append(ip)
607 added.append(ip)
598 except formencode.Invalid as error:
608 except formencode.Invalid as error:
599 msg = error.error_dict['ip']
609 msg = error.error_dict['ip']
600 h.flash(msg, category='error')
610 h.flash(msg, category='error')
601 except Exception:
611 except Exception:
602 log.exception("Exception during ip saving")
612 log.exception("Exception during ip saving")
603 h.flash(_('An error occurred during ip saving'),
613 h.flash(_('An error occurred during ip saving'),
604 category='error')
614 category='error')
605 if added:
615 if added:
606 h.flash(
616 h.flash(
607 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
617 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
608 category='success')
618 category='success')
609 if 'default_user' in request.POST:
619 if 'default_user' in request.POST:
610 return redirect(url('admin_permissions_ips'))
620 return redirect(url('admin_permissions_ips'))
611 return redirect(url('edit_user_ips', user_id=user_id))
621 return redirect(url('edit_user_ips', user_id=user_id))
612
622
613 @HasPermissionAllDecorator('hg.admin')
623 @HasPermissionAllDecorator('hg.admin')
614 @auth.CSRFRequired()
624 @auth.CSRFRequired()
615 def delete_ip(self, user_id):
625 def delete_ip(self, user_id):
616 """DELETE /user_ips_delete/user_id: Delete an existing item"""
617 # url('user_ips_delete', user_id=ID, method='delete')
618 user_id = safe_int(user_id)
626 user_id = safe_int(user_id)
619 c.user = User.get_or_404(user_id)
627 c.user = User.get_or_404(user_id)
620
628
621 ip_id = request.POST.get('del_ip_id')
629 ip_id = request.POST.get('del_ip_id')
622 user_model = UserModel()
630 user_model = UserModel()
631 ip = UserIpMap.query().get(ip_id).ip_addr
632 user_data = c.user.get_api_data()
623 user_model.delete_extra_ip(user_id, ip_id)
633 user_model.delete_extra_ip(user_id, ip_id)
634 audit_logger.store_web(
635 'user.edit.ip.delete',
636 action_data={'ip': ip, 'user': user_data},
637 user=c.rhodecode_user)
624 Session().commit()
638 Session().commit()
625 h.flash(_("Removed ip address from user whitelist"), category='success')
639 h.flash(_("Removed ip address from user whitelist"), category='success')
626
640
627 if 'default_user' in request.POST:
641 if 'default_user' in request.POST:
628 return redirect(url('admin_permissions_ips'))
642 return redirect(url('admin_permissions_ips'))
629 return redirect(url('edit_user_ips', user_id=user_id))
643 return redirect(url('edit_user_ips', user_id=user_id))
@@ -1,219 +1,231 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2017 RhodeCode GmbH
3 # Copyright (C) 2017-2017 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
23
24 from rhodecode.model import meta
24 from rhodecode.model import meta
25 from rhodecode.model.db import User, UserLog, Repository
25 from rhodecode.model.db import User, UserLog, Repository
26
26
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30 # action as key, and expected action_data as value
31 ACTIONS = {
31 ACTIONS = {
32 'user.login.success': {},
32 'user.login.success': {},
33 'user.login.failure': {},
33 'user.login.failure': {},
34 'user.logout': {},
34 'user.logout': {},
35 'user.password.reset_request': {},
35 'user.password.reset_request': {},
36 'user.push': {},
36 'user.push': {},
37 'user.pull': {},
37 'user.pull': {},
38
38
39 'repo.create': {},
39 'repo.create': {},
40 'repo.edit': {},
40 'repo.edit': {},
41 'user.create': {'data': {}},
42 'user.delete': {'old_data': {}},
43 'user.edit': {'old_data': {}},
44 'user.edit.permissions': {},
45 'user.edit.ip.add': {},
46 'user.edit.ip.delete': {},
47 'user.edit.token.add': {},
48 'user.edit.token.delete': {},
49 'user.edit.email.add': {},
50 'user.edit.email.delete': {},
51 'user.edit.password_reset.enabled': {},
52 'user.edit.password_reset.disabled': {},
53
41 'repo.edit.permissions': {},
54 'repo.edit.permissions': {},
42 'repo.delete': {},
55 'repo.delete': {},
43 'repo.commit.strip': {},
56 'repo.commit.strip': {},
44 'repo.archive.download': {},
57 'repo.archive.download': {},
45
58
46 'repo_group.create': {},
59 'repo_group.create': {},
47 'repo_group.edit': {},
60 'repo_group.edit': {},
48 'repo_group.edit.permissions': {},
61 'repo_group.edit.permissions': {},
49 'repo_group.delete': {},
62 'repo_group.delete': {},
50 }
63 }
51
64
52 SOURCE_WEB = 'source_web'
65 SOURCE_WEB = 'source_web'
53 SOURCE_API = 'source_api'
66 SOURCE_API = 'source_api'
54
67
55
68
56 class UserWrap(object):
69 class UserWrap(object):
57 """
70 """
58 Fake object used to imitate AuthUser
71 Fake object used to imitate AuthUser
59 """
72 """
60
73
61 def __init__(self, user_id=None, username=None, ip_addr=None):
74 def __init__(self, user_id=None, username=None, ip_addr=None):
62 self.user_id = user_id
75 self.user_id = user_id
63 self.username = username
76 self.username = username
64 self.ip_addr = ip_addr
77 self.ip_addr = ip_addr
65
78
66
79
67 class RepoWrap(object):
80 class RepoWrap(object):
68 """
81 """
69 Fake object used to imitate RepoObject that audit logger requires
82 Fake object used to imitate RepoObject that audit logger requires
70 """
83 """
71
84
72 def __init__(self, repo_id=None, repo_name=None):
85 def __init__(self, repo_id=None, repo_name=None):
73 self.repo_id = repo_id
86 self.repo_id = repo_id
74 self.repo_name = repo_name
87 self.repo_name = repo_name
75
88
76
89
77 def _store_log(action_name, action_data, user_id, username, user_data,
90 def _store_log(action_name, action_data, user_id, username, user_data,
78 ip_address, repository_id, repository_name):
91 ip_address, repository_id, repository_name):
79 user_log = UserLog()
92 user_log = UserLog()
80 user_log.version = UserLog.VERSION_2
93 user_log.version = UserLog.VERSION_2
81
94
82 user_log.action = action_name
95 user_log.action = action_name
83 user_log.action_data = action_data
96 user_log.action_data = action_data
84
97
85 user_log.user_ip = ip_address
98 user_log.user_ip = ip_address
86
99
87 user_log.user_id = user_id
100 user_log.user_id = user_id
88 user_log.username = username
101 user_log.username = username
89 user_log.user_data = user_data
102 user_log.user_data = user_data
90
103
91 user_log.repository_id = repository_id
104 user_log.repository_id = repository_id
92 user_log.repository_name = repository_name
105 user_log.repository_name = repository_name
93
106
94 user_log.action_date = datetime.datetime.now()
107 user_log.action_date = datetime.datetime.now()
95
108
96 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
109 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
97 action_name, user_id, username, ip_address)
110 action_name, user_id, username, ip_address)
98
111
99 return user_log
112 return user_log
100
113
101
114
102 def store_web(*args, **kwargs):
115 def store_web(*args, **kwargs):
103 if 'action_data' not in kwargs:
116 if 'action_data' not in kwargs:
104 kwargs['action_data'] = {}
117 kwargs['action_data'] = {}
105 kwargs['action_data'].update({
118 kwargs['action_data'].update({
106 'source': SOURCE_WEB
119 'source': SOURCE_WEB
107 })
120 })
108 return store(*args, **kwargs)
121 return store(*args, **kwargs)
109
122
110
123
111 def store_api(*args, **kwargs):
124 def store_api(*args, **kwargs):
112 if 'action_data' not in kwargs:
125 if 'action_data' not in kwargs:
113 kwargs['action_data'] = {}
126 kwargs['action_data'] = {}
114 kwargs['action_data'].update({
127 kwargs['action_data'].update({
115 'source': SOURCE_API
128 'source': SOURCE_API
116 })
129 })
117 return store(*args, **kwargs)
130 return store(*args, **kwargs)
118
131
119
132
120 def store(
133 def store(action, user, action_data=None, user_data=None, ip_addr=None,
121 action, user, action_data=None, user_data=None, ip_addr=None,
134 repo=None, sa_session=None, commit=False):
122 repo=None, sa_session=None, commit=False):
123 """
135 """
124 Audit logger for various actions made by users, typically this
136 Audit logger for various actions made by users, typically this
125 results in a call such::
137 results in a call such::
126
138
127 from rhodecode.lib import audit_logger
139 from rhodecode.lib import audit_logger
128
140
129 audit_logger.store(
141 audit_logger.store(
130 action='repo.edit', user=self._rhodecode_user)
142 action='repo.edit', user=self._rhodecode_user)
131 audit_logger.store(
143 audit_logger.store(
132 action='repo.delete', action_data={'repo_data': repo_data},
144 action='repo.delete', action_data={'repo_data': repo_data},
133 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
145 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
134
146
135 # repo action
147 # repo action
136 audit_logger.store(
148 audit_logger.store(
137 action='repo.delete',
149 action='repo.delete',
138 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
150 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
139 repo=audit_logger.RepoWrap(repo_name='some-repo'))
151 repo=audit_logger.RepoWrap(repo_name='some-repo'))
140
152
141 # repo action, when we know and have the repository object already
153 # repo action, when we know and have the repository object already
142 audit_logger.store(
154 audit_logger.store(
143 action='repo.delete',
155 action='repo.delete',
144 action_data={'source': audit_logger.SOURCE_WEB, },
156 action_data={'source': audit_logger.SOURCE_WEB, },
145 user=self._rhodecode_user,
157 user=self._rhodecode_user,
146 repo=repo_object)
158 repo=repo_object)
147
159
148 # alternative wrapper to the above
160 # alternative wrapper to the above
149 audit_logger.store_web(
161 audit_logger.store_web(
150 action='repo.delete',
162 action='repo.delete',
151 action_data={},
163 action_data={},
152 user=self._rhodecode_user,
164 user=self._rhodecode_user,
153 repo=repo_object)
165 repo=repo_object)
154
166
155 # without an user ?
167 # without an user ?
156 audit_logger.store(
168 audit_logger.store(
157 action='user.login.failure',
169 action='user.login.failure',
158 user=audit_logger.UserWrap(
170 user=audit_logger.UserWrap(
159 username=self.request.params.get('username'),
171 username=self.request.params.get('username'),
160 ip_addr=self.request.remote_addr))
172 ip_addr=self.request.remote_addr))
161
173
162 """
174 """
163 from rhodecode.lib.utils2 import safe_unicode
175 from rhodecode.lib.utils2 import safe_unicode
164 from rhodecode.lib.auth import AuthUser
176 from rhodecode.lib.auth import AuthUser
165
177
166 if action not in ACTIONS:
178 if action not in ACTIONS:
167 raise ValueError('Action `{}` not in valid actions'.format(action))
179 raise ValueError('Action `{}` not in valid actions'.format(action))
168
180
169 if not sa_session:
181 if not sa_session:
170 sa_session = meta.Session()
182 sa_session = meta.Session()
171
183
172 try:
184 try:
173 username = getattr(user, 'username', None)
185 username = getattr(user, 'username', None)
174 if not username:
186 if not username:
175 pass
187 pass
176
188
177 user_id = getattr(user, 'user_id', None)
189 user_id = getattr(user, 'user_id', None)
178 if not user_id:
190 if not user_id:
179 # maybe we have username ? Try to figure user_id from username
191 # maybe we have username ? Try to figure user_id from username
180 if username:
192 if username:
181 user_id = getattr(
193 user_id = getattr(
182 User.get_by_username(username), 'user_id', None)
194 User.get_by_username(username), 'user_id', None)
183
195
184 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
196 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
185 if not ip_addr:
197 if not ip_addr:
186 pass
198 pass
187
199
188 if not user_data:
200 if not user_data:
189 # try to get this from the auth user
201 # try to get this from the auth user
190 if isinstance(user, AuthUser):
202 if isinstance(user, AuthUser):
191 user_data = {
203 user_data = {
192 'username': user.username,
204 'username': user.username,
193 'email': user.email,
205 'email': user.email,
194 }
206 }
195
207
196 repository_name = getattr(repo, 'repo_name', None)
208 repository_name = getattr(repo, 'repo_name', None)
197 repository_id = getattr(repo, 'repo_id', None)
209 repository_id = getattr(repo, 'repo_id', None)
198 if not repository_id:
210 if not repository_id:
199 # maybe we have repo_name ? Try to figure repo_id from repo_name
211 # maybe we have repo_name ? Try to figure repo_id from repo_name
200 if repository_name:
212 if repository_name:
201 repository_id = getattr(
213 repository_id = getattr(
202 Repository.get_by_repo_name(repository_name), 'repo_id', None)
214 Repository.get_by_repo_name(repository_name), 'repo_id', None)
203
215
204 user_log = _store_log(
216 user_log = _store_log(
205 action_name=safe_unicode(action),
217 action_name=safe_unicode(action),
206 action_data=action_data or {},
218 action_data=action_data or {},
207 user_id=user_id,
219 user_id=user_id,
208 username=username,
220 username=username,
209 user_data=user_data or {},
221 user_data=user_data or {},
210 ip_address=safe_unicode(ip_addr),
222 ip_address=safe_unicode(ip_addr),
211 repository_id=repository_id,
223 repository_id=repository_id,
212 repository_name=repository_name
224 repository_name=repository_name
213 )
225 )
214 sa_session.add(user_log)
226 sa_session.add(user_log)
215 if commit:
227 if commit:
216 sa_session.commit()
228 sa_session.commit()
217
229
218 except Exception:
230 except Exception:
219 log.exception('AUDIT: failed to store audit log')
231 log.exception('AUDIT: failed to store audit log')
@@ -1,902 +1,902 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 users model for RhodeCode
22 users model for RhodeCode
23 """
23 """
24
24
25 import logging
25 import logging
26 import traceback
26 import traceback
27
27
28 import datetime
28 import datetime
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 import ipaddress
31 import ipaddress
32 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.exc import DatabaseError
33
33
34 from rhodecode import events
34 from rhodecode import events
35 from rhodecode.lib.user_log_filter import user_log_filter
35 from rhodecode.lib.user_log_filter import user_log_filter
36 from rhodecode.lib.utils2 import (
36 from rhodecode.lib.utils2 import (
37 safe_unicode, get_current_rhodecode_user, action_logger_generic,
37 safe_unicode, get_current_rhodecode_user, action_logger_generic,
38 AttributeDict, str2bool)
38 AttributeDict, str2bool)
39 from rhodecode.lib.exceptions import (
39 from rhodecode.lib.exceptions import (
40 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
40 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
41 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
42 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.lib.caching_query import FromCache
43 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
44 from rhodecode.model.auth_token import AuthTokenModel
44 from rhodecode.model.auth_token import AuthTokenModel
45 from rhodecode.model.db import (
45 from rhodecode.model.db import (
46 _hash_key, true, false, or_, joinedload, User, UserToPerm,
46 _hash_key, true, false, or_, joinedload, User, UserToPerm,
47 UserEmailMap, UserIpMap, UserLog)
47 UserEmailMap, UserIpMap, UserLog)
48 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
49 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.repo_group import RepoGroupModel
50
50
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class UserModel(BaseModel):
55 class UserModel(BaseModel):
56 cls = User
56 cls = User
57
57
58 def get(self, user_id, cache=False):
58 def get(self, user_id, cache=False):
59 user = self.sa.query(User)
59 user = self.sa.query(User)
60 if cache:
60 if cache:
61 user = user.options(
61 user = user.options(
62 FromCache("sql_cache_short", "get_user_%s" % user_id))
62 FromCache("sql_cache_short", "get_user_%s" % user_id))
63 return user.get(user_id)
63 return user.get(user_id)
64
64
65 def get_user(self, user):
65 def get_user(self, user):
66 return self._get_user(user)
66 return self._get_user(user)
67
67
68 def _serialize_user(self, user):
68 def _serialize_user(self, user):
69 import rhodecode.lib.helpers as h
69 import rhodecode.lib.helpers as h
70
70
71 return {
71 return {
72 'id': user.user_id,
72 'id': user.user_id,
73 'first_name': h.escape(user.name),
73 'first_name': h.escape(user.name),
74 'last_name': h.escape(user.lastname),
74 'last_name': h.escape(user.lastname),
75 'username': user.username,
75 'username': user.username,
76 'email': user.email,
76 'email': user.email,
77 'icon_link': h.gravatar_url(user.email, 30),
77 'icon_link': h.gravatar_url(user.email, 30),
78 'value_display': h.escape(h.person(user)),
78 'value_display': h.escape(h.person(user)),
79 'value': user.username,
79 'value': user.username,
80 'value_type': 'user',
80 'value_type': 'user',
81 'active': user.active,
81 'active': user.active,
82 }
82 }
83
83
84 def get_users(self, name_contains=None, limit=20, only_active=True):
84 def get_users(self, name_contains=None, limit=20, only_active=True):
85
85
86 query = self.sa.query(User)
86 query = self.sa.query(User)
87 if only_active:
87 if only_active:
88 query = query.filter(User.active == true())
88 query = query.filter(User.active == true())
89
89
90 if name_contains:
90 if name_contains:
91 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
91 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
92 query = query.filter(
92 query = query.filter(
93 or_(
93 or_(
94 User.name.ilike(ilike_expression),
94 User.name.ilike(ilike_expression),
95 User.lastname.ilike(ilike_expression),
95 User.lastname.ilike(ilike_expression),
96 User.username.ilike(ilike_expression)
96 User.username.ilike(ilike_expression)
97 )
97 )
98 )
98 )
99 query = query.limit(limit)
99 query = query.limit(limit)
100 users = query.all()
100 users = query.all()
101
101
102 _users = [
102 _users = [
103 self._serialize_user(user) for user in users
103 self._serialize_user(user) for user in users
104 ]
104 ]
105 return _users
105 return _users
106
106
107 def get_by_username(self, username, cache=False, case_insensitive=False):
107 def get_by_username(self, username, cache=False, case_insensitive=False):
108
108
109 if case_insensitive:
109 if case_insensitive:
110 user = self.sa.query(User).filter(User.username.ilike(username))
110 user = self.sa.query(User).filter(User.username.ilike(username))
111 else:
111 else:
112 user = self.sa.query(User)\
112 user = self.sa.query(User)\
113 .filter(User.username == username)
113 .filter(User.username == username)
114 if cache:
114 if cache:
115 name_key = _hash_key(username)
115 name_key = _hash_key(username)
116 user = user.options(
116 user = user.options(
117 FromCache("sql_cache_short", "get_user_%s" % name_key))
117 FromCache("sql_cache_short", "get_user_%s" % name_key))
118 return user.scalar()
118 return user.scalar()
119
119
120 def get_by_email(self, email, cache=False, case_insensitive=False):
120 def get_by_email(self, email, cache=False, case_insensitive=False):
121 return User.get_by_email(email, case_insensitive, cache)
121 return User.get_by_email(email, case_insensitive, cache)
122
122
123 def get_by_auth_token(self, auth_token, cache=False):
123 def get_by_auth_token(self, auth_token, cache=False):
124 return User.get_by_auth_token(auth_token, cache)
124 return User.get_by_auth_token(auth_token, cache)
125
125
126 def get_active_user_count(self, cache=False):
126 def get_active_user_count(self, cache=False):
127 return User.query().filter(
127 return User.query().filter(
128 User.active == True).filter(
128 User.active == True).filter(
129 User.username != User.DEFAULT_USER).count()
129 User.username != User.DEFAULT_USER).count()
130
130
131 def create(self, form_data, cur_user=None):
131 def create(self, form_data, cur_user=None):
132 if not cur_user:
132 if not cur_user:
133 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
133 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
134
134
135 user_data = {
135 user_data = {
136 'username': form_data['username'],
136 'username': form_data['username'],
137 'password': form_data['password'],
137 'password': form_data['password'],
138 'email': form_data['email'],
138 'email': form_data['email'],
139 'firstname': form_data['firstname'],
139 'firstname': form_data['firstname'],
140 'lastname': form_data['lastname'],
140 'lastname': form_data['lastname'],
141 'active': form_data['active'],
141 'active': form_data['active'],
142 'extern_type': form_data['extern_type'],
142 'extern_type': form_data['extern_type'],
143 'extern_name': form_data['extern_name'],
143 'extern_name': form_data['extern_name'],
144 'admin': False,
144 'admin': False,
145 'cur_user': cur_user
145 'cur_user': cur_user
146 }
146 }
147
147
148 if 'create_repo_group' in form_data:
148 if 'create_repo_group' in form_data:
149 user_data['create_repo_group'] = str2bool(
149 user_data['create_repo_group'] = str2bool(
150 form_data.get('create_repo_group'))
150 form_data.get('create_repo_group'))
151
151
152 try:
152 try:
153 if form_data.get('password_change'):
153 if form_data.get('password_change'):
154 user_data['force_password_change'] = True
154 user_data['force_password_change'] = True
155 return UserModel().create_or_update(**user_data)
155 return UserModel().create_or_update(**user_data)
156 except Exception:
156 except Exception:
157 log.error(traceback.format_exc())
157 log.error(traceback.format_exc())
158 raise
158 raise
159
159
160 def update_user(self, user, skip_attrs=None, **kwargs):
160 def update_user(self, user, skip_attrs=None, **kwargs):
161 from rhodecode.lib.auth import get_crypt_password
161 from rhodecode.lib.auth import get_crypt_password
162
162
163 user = self._get_user(user)
163 user = self._get_user(user)
164 if user.username == User.DEFAULT_USER:
164 if user.username == User.DEFAULT_USER:
165 raise DefaultUserException(
165 raise DefaultUserException(
166 _("You can't Edit this user since it's"
166 _("You can't Edit this user since it's"
167 " crucial for entire application"))
167 " crucial for entire application"))
168
168
169 # first store only defaults
169 # first store only defaults
170 user_attrs = {
170 user_attrs = {
171 'updating_user_id': user.user_id,
171 'updating_user_id': user.user_id,
172 'username': user.username,
172 'username': user.username,
173 'password': user.password,
173 'password': user.password,
174 'email': user.email,
174 'email': user.email,
175 'firstname': user.name,
175 'firstname': user.name,
176 'lastname': user.lastname,
176 'lastname': user.lastname,
177 'active': user.active,
177 'active': user.active,
178 'admin': user.admin,
178 'admin': user.admin,
179 'extern_name': user.extern_name,
179 'extern_name': user.extern_name,
180 'extern_type': user.extern_type,
180 'extern_type': user.extern_type,
181 'language': user.user_data.get('language')
181 'language': user.user_data.get('language')
182 }
182 }
183
183
184 # in case there's new_password, that comes from form, use it to
184 # in case there's new_password, that comes from form, use it to
185 # store password
185 # store password
186 if kwargs.get('new_password'):
186 if kwargs.get('new_password'):
187 kwargs['password'] = kwargs['new_password']
187 kwargs['password'] = kwargs['new_password']
188
188
189 # cleanups, my_account password change form
189 # cleanups, my_account password change form
190 kwargs.pop('current_password', None)
190 kwargs.pop('current_password', None)
191 kwargs.pop('new_password', None)
191 kwargs.pop('new_password', None)
192
192
193 # cleanups, user edit password change form
193 # cleanups, user edit password change form
194 kwargs.pop('password_confirmation', None)
194 kwargs.pop('password_confirmation', None)
195 kwargs.pop('password_change', None)
195 kwargs.pop('password_change', None)
196
196
197 # create repo group on user creation
197 # create repo group on user creation
198 kwargs.pop('create_repo_group', None)
198 kwargs.pop('create_repo_group', None)
199
199
200 # legacy forms send name, which is the firstname
200 # legacy forms send name, which is the firstname
201 firstname = kwargs.pop('name', None)
201 firstname = kwargs.pop('name', None)
202 if firstname:
202 if firstname:
203 kwargs['firstname'] = firstname
203 kwargs['firstname'] = firstname
204
204
205 for k, v in kwargs.items():
205 for k, v in kwargs.items():
206 # skip if we don't want to update this
206 # skip if we don't want to update this
207 if skip_attrs and k in skip_attrs:
207 if skip_attrs and k in skip_attrs:
208 continue
208 continue
209
209
210 user_attrs[k] = v
210 user_attrs[k] = v
211
211
212 try:
212 try:
213 return self.create_or_update(**user_attrs)
213 return self.create_or_update(**user_attrs)
214 except Exception:
214 except Exception:
215 log.error(traceback.format_exc())
215 log.error(traceback.format_exc())
216 raise
216 raise
217
217
218 def create_or_update(
218 def create_or_update(
219 self, username, password, email, firstname='', lastname='',
219 self, username, password, email, firstname='', lastname='',
220 active=True, admin=False, extern_type=None, extern_name=None,
220 active=True, admin=False, extern_type=None, extern_name=None,
221 cur_user=None, plugin=None, force_password_change=False,
221 cur_user=None, plugin=None, force_password_change=False,
222 allow_to_create_user=True, create_repo_group=None,
222 allow_to_create_user=True, create_repo_group=None,
223 updating_user_id=None, language=None, strict_creation_check=True):
223 updating_user_id=None, language=None, strict_creation_check=True):
224 """
224 """
225 Creates a new instance if not found, or updates current one
225 Creates a new instance if not found, or updates current one
226
226
227 :param username:
227 :param username:
228 :param password:
228 :param password:
229 :param email:
229 :param email:
230 :param firstname:
230 :param firstname:
231 :param lastname:
231 :param lastname:
232 :param active:
232 :param active:
233 :param admin:
233 :param admin:
234 :param extern_type:
234 :param extern_type:
235 :param extern_name:
235 :param extern_name:
236 :param cur_user:
236 :param cur_user:
237 :param plugin: optional plugin this method was called from
237 :param plugin: optional plugin this method was called from
238 :param force_password_change: toggles new or existing user flag
238 :param force_password_change: toggles new or existing user flag
239 for password change
239 for password change
240 :param allow_to_create_user: Defines if the method can actually create
240 :param allow_to_create_user: Defines if the method can actually create
241 new users
241 new users
242 :param create_repo_group: Defines if the method should also
242 :param create_repo_group: Defines if the method should also
243 create an repo group with user name, and owner
243 create an repo group with user name, and owner
244 :param updating_user_id: if we set it up this is the user we want to
244 :param updating_user_id: if we set it up this is the user we want to
245 update this allows to editing username.
245 update this allows to editing username.
246 :param language: language of user from interface.
246 :param language: language of user from interface.
247
247
248 :returns: new User object with injected `is_new_user` attribute.
248 :returns: new User object with injected `is_new_user` attribute.
249 """
249 """
250 if not cur_user:
250 if not cur_user:
251 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
251 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
252
252
253 from rhodecode.lib.auth import (
253 from rhodecode.lib.auth import (
254 get_crypt_password, check_password, generate_auth_token)
254 get_crypt_password, check_password, generate_auth_token)
255 from rhodecode.lib.hooks_base import (
255 from rhodecode.lib.hooks_base import (
256 log_create_user, check_allowed_create_user)
256 log_create_user, check_allowed_create_user)
257
257
258 def _password_change(new_user, password):
258 def _password_change(new_user, password):
259 # empty password
259 # empty password
260 if not new_user.password:
260 if not new_user.password:
261 return False
261 return False
262
262
263 # password check is only needed for RhodeCode internal auth calls
263 # password check is only needed for RhodeCode internal auth calls
264 # in case it's a plugin we don't care
264 # in case it's a plugin we don't care
265 if not plugin:
265 if not plugin:
266
266
267 # first check if we gave crypted password back, and if it
267 # first check if we gave crypted password back, and if it
268 # matches it's not password change
268 # matches it's not password change
269 if new_user.password == password:
269 if new_user.password == password:
270 return False
270 return False
271
271
272 password_match = check_password(password, new_user.password)
272 password_match = check_password(password, new_user.password)
273 if not password_match:
273 if not password_match:
274 return True
274 return True
275
275
276 return False
276 return False
277
277
278 # read settings on default personal repo group creation
278 # read settings on default personal repo group creation
279 if create_repo_group is None:
279 if create_repo_group is None:
280 default_create_repo_group = RepoGroupModel()\
280 default_create_repo_group = RepoGroupModel()\
281 .get_default_create_personal_repo_group()
281 .get_default_create_personal_repo_group()
282 create_repo_group = default_create_repo_group
282 create_repo_group = default_create_repo_group
283
283
284 user_data = {
284 user_data = {
285 'username': username,
285 'username': username,
286 'password': password,
286 'password': password,
287 'email': email,
287 'email': email,
288 'firstname': firstname,
288 'firstname': firstname,
289 'lastname': lastname,
289 'lastname': lastname,
290 'active': active,
290 'active': active,
291 'admin': admin
291 'admin': admin
292 }
292 }
293
293
294 if updating_user_id:
294 if updating_user_id:
295 log.debug('Checking for existing account in RhodeCode '
295 log.debug('Checking for existing account in RhodeCode '
296 'database with user_id `%s` ' % (updating_user_id,))
296 'database with user_id `%s` ' % (updating_user_id,))
297 user = User.get(updating_user_id)
297 user = User.get(updating_user_id)
298 else:
298 else:
299 log.debug('Checking for existing account in RhodeCode '
299 log.debug('Checking for existing account in RhodeCode '
300 'database with username `%s` ' % (username,))
300 'database with username `%s` ' % (username,))
301 user = User.get_by_username(username, case_insensitive=True)
301 user = User.get_by_username(username, case_insensitive=True)
302
302
303 if user is None:
303 if user is None:
304 # we check internal flag if this method is actually allowed to
304 # we check internal flag if this method is actually allowed to
305 # create new user
305 # create new user
306 if not allow_to_create_user:
306 if not allow_to_create_user:
307 msg = ('Method wants to create new user, but it is not '
307 msg = ('Method wants to create new user, but it is not '
308 'allowed to do so')
308 'allowed to do so')
309 log.warning(msg)
309 log.warning(msg)
310 raise NotAllowedToCreateUserError(msg)
310 raise NotAllowedToCreateUserError(msg)
311
311
312 log.debug('Creating new user %s', username)
312 log.debug('Creating new user %s', username)
313
313
314 # only if we create user that is active
314 # only if we create user that is active
315 new_active_user = active
315 new_active_user = active
316 if new_active_user and strict_creation_check:
316 if new_active_user and strict_creation_check:
317 # raises UserCreationError if it's not allowed for any reason to
317 # raises UserCreationError if it's not allowed for any reason to
318 # create new active user, this also executes pre-create hooks
318 # create new active user, this also executes pre-create hooks
319 check_allowed_create_user(user_data, cur_user, strict_check=True)
319 check_allowed_create_user(user_data, cur_user, strict_check=True)
320 events.trigger(events.UserPreCreate(user_data))
320 events.trigger(events.UserPreCreate(user_data))
321 new_user = User()
321 new_user = User()
322 edit = False
322 edit = False
323 else:
323 else:
324 log.debug('updating user %s', username)
324 log.debug('updating user %s', username)
325 events.trigger(events.UserPreUpdate(user, user_data))
325 events.trigger(events.UserPreUpdate(user, user_data))
326 new_user = user
326 new_user = user
327 edit = True
327 edit = True
328
328
329 # we're not allowed to edit default user
329 # we're not allowed to edit default user
330 if user.username == User.DEFAULT_USER:
330 if user.username == User.DEFAULT_USER:
331 raise DefaultUserException(
331 raise DefaultUserException(
332 _("You can't edit this user (`%(username)s`) since it's "
332 _("You can't edit this user (`%(username)s`) since it's "
333 "crucial for entire application") % {'username': user.username})
333 "crucial for entire application") % {'username': user.username})
334
334
335 # inject special attribute that will tell us if User is new or old
335 # inject special attribute that will tell us if User is new or old
336 new_user.is_new_user = not edit
336 new_user.is_new_user = not edit
337 # for users that didn's specify auth type, we use RhodeCode built in
337 # for users that didn's specify auth type, we use RhodeCode built in
338 from rhodecode.authentication.plugins import auth_rhodecode
338 from rhodecode.authentication.plugins import auth_rhodecode
339 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
339 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
340 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
340 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
341
341
342 try:
342 try:
343 new_user.username = username
343 new_user.username = username
344 new_user.admin = admin
344 new_user.admin = admin
345 new_user.email = email
345 new_user.email = email
346 new_user.active = active
346 new_user.active = active
347 new_user.extern_name = safe_unicode(extern_name)
347 new_user.extern_name = safe_unicode(extern_name)
348 new_user.extern_type = safe_unicode(extern_type)
348 new_user.extern_type = safe_unicode(extern_type)
349 new_user.name = firstname
349 new_user.name = firstname
350 new_user.lastname = lastname
350 new_user.lastname = lastname
351
351
352 # set password only if creating an user or password is changed
352 # set password only if creating an user or password is changed
353 if not edit or _password_change(new_user, password):
353 if not edit or _password_change(new_user, password):
354 reason = 'new password' if edit else 'new user'
354 reason = 'new password' if edit else 'new user'
355 log.debug('Updating password reason=>%s', reason)
355 log.debug('Updating password reason=>%s', reason)
356 new_user.password = get_crypt_password(password) if password else None
356 new_user.password = get_crypt_password(password) if password else None
357
357
358 if force_password_change:
358 if force_password_change:
359 new_user.update_userdata(force_password_change=True)
359 new_user.update_userdata(force_password_change=True)
360 if language:
360 if language:
361 new_user.update_userdata(language=language)
361 new_user.update_userdata(language=language)
362 new_user.update_userdata(notification_status=True)
362 new_user.update_userdata(notification_status=True)
363
363
364 self.sa.add(new_user)
364 self.sa.add(new_user)
365
365
366 if not edit and create_repo_group:
366 if not edit and create_repo_group:
367 RepoGroupModel().create_personal_repo_group(
367 RepoGroupModel().create_personal_repo_group(
368 new_user, commit_early=False)
368 new_user, commit_early=False)
369
369
370 if not edit:
370 if not edit:
371 # add the RSS token
371 # add the RSS token
372 AuthTokenModel().create(username,
372 AuthTokenModel().create(username,
373 description='Generated feed token',
373 description='Generated feed token',
374 role=AuthTokenModel.cls.ROLE_FEED)
374 role=AuthTokenModel.cls.ROLE_FEED)
375 log_create_user(created_by=cur_user, **new_user.get_dict())
375 log_create_user(created_by=cur_user, **new_user.get_dict())
376 events.trigger(events.UserPostCreate(user_data))
376 events.trigger(events.UserPostCreate(user_data))
377 return new_user
377 return new_user
378 except (DatabaseError,):
378 except (DatabaseError,):
379 log.error(traceback.format_exc())
379 log.error(traceback.format_exc())
380 raise
380 raise
381
381
382 def create_registration(self, form_data):
382 def create_registration(self, form_data):
383 from rhodecode.model.notification import NotificationModel
383 from rhodecode.model.notification import NotificationModel
384 from rhodecode.model.notification import EmailNotificationModel
384 from rhodecode.model.notification import EmailNotificationModel
385
385
386 try:
386 try:
387 form_data['admin'] = False
387 form_data['admin'] = False
388 form_data['extern_name'] = 'rhodecode'
388 form_data['extern_name'] = 'rhodecode'
389 form_data['extern_type'] = 'rhodecode'
389 form_data['extern_type'] = 'rhodecode'
390 new_user = self.create(form_data)
390 new_user = self.create(form_data)
391
391
392 self.sa.add(new_user)
392 self.sa.add(new_user)
393 self.sa.flush()
393 self.sa.flush()
394
394
395 user_data = new_user.get_dict()
395 user_data = new_user.get_dict()
396 kwargs = {
396 kwargs = {
397 # use SQLALCHEMY safe dump of user data
397 # use SQLALCHEMY safe dump of user data
398 'user': AttributeDict(user_data),
398 'user': AttributeDict(user_data),
399 'date': datetime.datetime.now()
399 'date': datetime.datetime.now()
400 }
400 }
401 notification_type = EmailNotificationModel.TYPE_REGISTRATION
401 notification_type = EmailNotificationModel.TYPE_REGISTRATION
402 # pre-generate the subject for notification itself
402 # pre-generate the subject for notification itself
403 (subject,
403 (subject,
404 _h, _e, # we don't care about those
404 _h, _e, # we don't care about those
405 body_plaintext) = EmailNotificationModel().render_email(
405 body_plaintext) = EmailNotificationModel().render_email(
406 notification_type, **kwargs)
406 notification_type, **kwargs)
407
407
408 # create notification objects, and emails
408 # create notification objects, and emails
409 NotificationModel().create(
409 NotificationModel().create(
410 created_by=new_user,
410 created_by=new_user,
411 notification_subject=subject,
411 notification_subject=subject,
412 notification_body=body_plaintext,
412 notification_body=body_plaintext,
413 notification_type=notification_type,
413 notification_type=notification_type,
414 recipients=None, # all admins
414 recipients=None, # all admins
415 email_kwargs=kwargs,
415 email_kwargs=kwargs,
416 )
416 )
417
417
418 return new_user
418 return new_user
419 except Exception:
419 except Exception:
420 log.error(traceback.format_exc())
420 log.error(traceback.format_exc())
421 raise
421 raise
422
422
423 def _handle_user_repos(self, username, repositories, handle_mode=None):
423 def _handle_user_repos(self, username, repositories, handle_mode=None):
424 _superadmin = self.cls.get_first_super_admin()
424 _superadmin = self.cls.get_first_super_admin()
425 left_overs = True
425 left_overs = True
426
426
427 from rhodecode.model.repo import RepoModel
427 from rhodecode.model.repo import RepoModel
428
428
429 if handle_mode == 'detach':
429 if handle_mode == 'detach':
430 for obj in repositories:
430 for obj in repositories:
431 obj.user = _superadmin
431 obj.user = _superadmin
432 # set description we know why we super admin now owns
432 # set description we know why we super admin now owns
433 # additional repositories that were orphaned !
433 # additional repositories that were orphaned !
434 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
434 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
435 self.sa.add(obj)
435 self.sa.add(obj)
436 left_overs = False
436 left_overs = False
437 elif handle_mode == 'delete':
437 elif handle_mode == 'delete':
438 for obj in repositories:
438 for obj in repositories:
439 RepoModel().delete(obj, forks='detach')
439 RepoModel().delete(obj, forks='detach')
440 left_overs = False
440 left_overs = False
441
441
442 # if nothing is done we have left overs left
442 # if nothing is done we have left overs left
443 return left_overs
443 return left_overs
444
444
445 def _handle_user_repo_groups(self, username, repository_groups,
445 def _handle_user_repo_groups(self, username, repository_groups,
446 handle_mode=None):
446 handle_mode=None):
447 _superadmin = self.cls.get_first_super_admin()
447 _superadmin = self.cls.get_first_super_admin()
448 left_overs = True
448 left_overs = True
449
449
450 from rhodecode.model.repo_group import RepoGroupModel
450 from rhodecode.model.repo_group import RepoGroupModel
451
451
452 if handle_mode == 'detach':
452 if handle_mode == 'detach':
453 for r in repository_groups:
453 for r in repository_groups:
454 r.user = _superadmin
454 r.user = _superadmin
455 # set description we know why we super admin now owns
455 # set description we know why we super admin now owns
456 # additional repositories that were orphaned !
456 # additional repositories that were orphaned !
457 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
457 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
458 self.sa.add(r)
458 self.sa.add(r)
459 left_overs = False
459 left_overs = False
460 elif handle_mode == 'delete':
460 elif handle_mode == 'delete':
461 for r in repository_groups:
461 for r in repository_groups:
462 RepoGroupModel().delete(r)
462 RepoGroupModel().delete(r)
463 left_overs = False
463 left_overs = False
464
464
465 # if nothing is done we have left overs left
465 # if nothing is done we have left overs left
466 return left_overs
466 return left_overs
467
467
468 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
468 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
469 _superadmin = self.cls.get_first_super_admin()
469 _superadmin = self.cls.get_first_super_admin()
470 left_overs = True
470 left_overs = True
471
471
472 from rhodecode.model.user_group import UserGroupModel
472 from rhodecode.model.user_group import UserGroupModel
473
473
474 if handle_mode == 'detach':
474 if handle_mode == 'detach':
475 for r in user_groups:
475 for r in user_groups:
476 for user_user_group_to_perm in r.user_user_group_to_perm:
476 for user_user_group_to_perm in r.user_user_group_to_perm:
477 if user_user_group_to_perm.user.username == username:
477 if user_user_group_to_perm.user.username == username:
478 user_user_group_to_perm.user = _superadmin
478 user_user_group_to_perm.user = _superadmin
479 r.user = _superadmin
479 r.user = _superadmin
480 # set description we know why we super admin now owns
480 # set description we know why we super admin now owns
481 # additional repositories that were orphaned !
481 # additional repositories that were orphaned !
482 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
482 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
483 self.sa.add(r)
483 self.sa.add(r)
484 left_overs = False
484 left_overs = False
485 elif handle_mode == 'delete':
485 elif handle_mode == 'delete':
486 for r in user_groups:
486 for r in user_groups:
487 UserGroupModel().delete(r)
487 UserGroupModel().delete(r)
488 left_overs = False
488 left_overs = False
489
489
490 # if nothing is done we have left overs left
490 # if nothing is done we have left overs left
491 return left_overs
491 return left_overs
492
492
493 def delete(self, user, cur_user=None, handle_repos=None,
493 def delete(self, user, cur_user=None, handle_repos=None,
494 handle_repo_groups=None, handle_user_groups=None):
494 handle_repo_groups=None, handle_user_groups=None):
495 if not cur_user:
495 if not cur_user:
496 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
496 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
497 user = self._get_user(user)
497 user = self._get_user(user)
498
498
499 try:
499 try:
500 if user.username == User.DEFAULT_USER:
500 if user.username == User.DEFAULT_USER:
501 raise DefaultUserException(
501 raise DefaultUserException(
502 _(u"You can't remove this user since it's"
502 _(u"You can't remove this user since it's"
503 u" crucial for entire application"))
503 u" crucial for entire application"))
504
504
505 left_overs = self._handle_user_repos(
505 left_overs = self._handle_user_repos(
506 user.username, user.repositories, handle_repos)
506 user.username, user.repositories, handle_repos)
507 if left_overs and user.repositories:
507 if left_overs and user.repositories:
508 repos = [x.repo_name for x in user.repositories]
508 repos = [x.repo_name for x in user.repositories]
509 raise UserOwnsReposException(
509 raise UserOwnsReposException(
510 _(u'user "%s" still owns %s repositories and cannot be '
510 _(u'user "%s" still owns %s repositories and cannot be '
511 u'removed. Switch owners or remove those repositories:%s')
511 u'removed. Switch owners or remove those repositories:%s')
512 % (user.username, len(repos), ', '.join(repos)))
512 % (user.username, len(repos), ', '.join(repos)))
513
513
514 left_overs = self._handle_user_repo_groups(
514 left_overs = self._handle_user_repo_groups(
515 user.username, user.repository_groups, handle_repo_groups)
515 user.username, user.repository_groups, handle_repo_groups)
516 if left_overs and user.repository_groups:
516 if left_overs and user.repository_groups:
517 repo_groups = [x.group_name for x in user.repository_groups]
517 repo_groups = [x.group_name for x in user.repository_groups]
518 raise UserOwnsRepoGroupsException(
518 raise UserOwnsRepoGroupsException(
519 _(u'user "%s" still owns %s repository groups and cannot be '
519 _(u'user "%s" still owns %s repository groups and cannot be '
520 u'removed. Switch owners or remove those repository groups:%s')
520 u'removed. Switch owners or remove those repository groups:%s')
521 % (user.username, len(repo_groups), ', '.join(repo_groups)))
521 % (user.username, len(repo_groups), ', '.join(repo_groups)))
522
522
523 left_overs = self._handle_user_user_groups(
523 left_overs = self._handle_user_user_groups(
524 user.username, user.user_groups, handle_user_groups)
524 user.username, user.user_groups, handle_user_groups)
525 if left_overs and user.user_groups:
525 if left_overs and user.user_groups:
526 user_groups = [x.users_group_name for x in user.user_groups]
526 user_groups = [x.users_group_name for x in user.user_groups]
527 raise UserOwnsUserGroupsException(
527 raise UserOwnsUserGroupsException(
528 _(u'user "%s" still owns %s user groups and cannot be '
528 _(u'user "%s" still owns %s user groups and cannot be '
529 u'removed. Switch owners or remove those user groups:%s')
529 u'removed. Switch owners or remove those user groups:%s')
530 % (user.username, len(user_groups), ', '.join(user_groups)))
530 % (user.username, len(user_groups), ', '.join(user_groups)))
531
531
532 # we might change the user data with detach/delete, make sure
532 # we might change the user data with detach/delete, make sure
533 # the object is marked as expired before actually deleting !
533 # the object is marked as expired before actually deleting !
534 self.sa.expire(user)
534 self.sa.expire(user)
535 self.sa.delete(user)
535 self.sa.delete(user)
536 from rhodecode.lib.hooks_base import log_delete_user
536 from rhodecode.lib.hooks_base import log_delete_user
537 log_delete_user(deleted_by=cur_user, **user.get_dict())
537 log_delete_user(deleted_by=cur_user, **user.get_dict())
538 except Exception:
538 except Exception:
539 log.error(traceback.format_exc())
539 log.error(traceback.format_exc())
540 raise
540 raise
541
541
542 def reset_password_link(self, data, pwd_reset_url):
542 def reset_password_link(self, data, pwd_reset_url):
543 from rhodecode.lib.celerylib import tasks, run_task
543 from rhodecode.lib.celerylib import tasks, run_task
544 from rhodecode.model.notification import EmailNotificationModel
544 from rhodecode.model.notification import EmailNotificationModel
545 user_email = data['email']
545 user_email = data['email']
546 try:
546 try:
547 user = User.get_by_email(user_email)
547 user = User.get_by_email(user_email)
548 if user:
548 if user:
549 log.debug('password reset user found %s', user)
549 log.debug('password reset user found %s', user)
550
550
551 email_kwargs = {
551 email_kwargs = {
552 'password_reset_url': pwd_reset_url,
552 'password_reset_url': pwd_reset_url,
553 'user': user,
553 'user': user,
554 'email': user_email,
554 'email': user_email,
555 'date': datetime.datetime.now()
555 'date': datetime.datetime.now()
556 }
556 }
557
557
558 (subject, headers, email_body,
558 (subject, headers, email_body,
559 email_body_plaintext) = EmailNotificationModel().render_email(
559 email_body_plaintext) = EmailNotificationModel().render_email(
560 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
560 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
561
561
562 recipients = [user_email]
562 recipients = [user_email]
563
563
564 action_logger_generic(
564 action_logger_generic(
565 'sending password reset email to user: {}'.format(
565 'sending password reset email to user: {}'.format(
566 user), namespace='security.password_reset')
566 user), namespace='security.password_reset')
567
567
568 run_task(tasks.send_email, recipients, subject,
568 run_task(tasks.send_email, recipients, subject,
569 email_body_plaintext, email_body)
569 email_body_plaintext, email_body)
570
570
571 else:
571 else:
572 log.debug("password reset email %s not found", user_email)
572 log.debug("password reset email %s not found", user_email)
573 except Exception:
573 except Exception:
574 log.error(traceback.format_exc())
574 log.error(traceback.format_exc())
575 return False
575 return False
576
576
577 return True
577 return True
578
578
579 def reset_password(self, data):
579 def reset_password(self, data):
580 from rhodecode.lib.celerylib import tasks, run_task
580 from rhodecode.lib.celerylib import tasks, run_task
581 from rhodecode.model.notification import EmailNotificationModel
581 from rhodecode.model.notification import EmailNotificationModel
582 from rhodecode.lib import auth
582 from rhodecode.lib import auth
583 user_email = data['email']
583 user_email = data['email']
584 pre_db = True
584 pre_db = True
585 try:
585 try:
586 user = User.get_by_email(user_email)
586 user = User.get_by_email(user_email)
587 new_passwd = auth.PasswordGenerator().gen_password(
587 new_passwd = auth.PasswordGenerator().gen_password(
588 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
588 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
589 if user:
589 if user:
590 user.password = auth.get_crypt_password(new_passwd)
590 user.password = auth.get_crypt_password(new_passwd)
591 # also force this user to reset his password !
591 # also force this user to reset his password !
592 user.update_userdata(force_password_change=True)
592 user.update_userdata(force_password_change=True)
593
593
594 Session().add(user)
594 Session().add(user)
595
595
596 # now delete the token in question
596 # now delete the token in question
597 UserApiKeys = AuthTokenModel.cls
597 UserApiKeys = AuthTokenModel.cls
598 UserApiKeys().query().filter(
598 UserApiKeys().query().filter(
599 UserApiKeys.api_key == data['token']).delete()
599 UserApiKeys.api_key == data['token']).delete()
600
600
601 Session().commit()
601 Session().commit()
602 log.info('successfully reset password for `%s`', user_email)
602 log.info('successfully reset password for `%s`', user_email)
603
603
604 if new_passwd is None:
604 if new_passwd is None:
605 raise Exception('unable to generate new password')
605 raise Exception('unable to generate new password')
606
606
607 pre_db = False
607 pre_db = False
608
608
609 email_kwargs = {
609 email_kwargs = {
610 'new_password': new_passwd,
610 'new_password': new_passwd,
611 'user': user,
611 'user': user,
612 'email': user_email,
612 'email': user_email,
613 'date': datetime.datetime.now()
613 'date': datetime.datetime.now()
614 }
614 }
615
615
616 (subject, headers, email_body,
616 (subject, headers, email_body,
617 email_body_plaintext) = EmailNotificationModel().render_email(
617 email_body_plaintext) = EmailNotificationModel().render_email(
618 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
618 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
619 **email_kwargs)
619 **email_kwargs)
620
620
621 recipients = [user_email]
621 recipients = [user_email]
622
622
623 action_logger_generic(
623 action_logger_generic(
624 'sent new password to user: {} with email: {}'.format(
624 'sent new password to user: {} with email: {}'.format(
625 user, user_email), namespace='security.password_reset')
625 user, user_email), namespace='security.password_reset')
626
626
627 run_task(tasks.send_email, recipients, subject,
627 run_task(tasks.send_email, recipients, subject,
628 email_body_plaintext, email_body)
628 email_body_plaintext, email_body)
629
629
630 except Exception:
630 except Exception:
631 log.error('Failed to update user password')
631 log.error('Failed to update user password')
632 log.error(traceback.format_exc())
632 log.error(traceback.format_exc())
633 if pre_db:
633 if pre_db:
634 # we rollback only if local db stuff fails. If it goes into
634 # we rollback only if local db stuff fails. If it goes into
635 # run_task, we're pass rollback state this wouldn't work then
635 # run_task, we're pass rollback state this wouldn't work then
636 Session().rollback()
636 Session().rollback()
637
637
638 return True
638 return True
639
639
640 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
640 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
641 """
641 """
642 Fetches auth_user by user_id,or api_key if present.
642 Fetches auth_user by user_id,or api_key if present.
643 Fills auth_user attributes with those taken from database.
643 Fills auth_user attributes with those taken from database.
644 Additionally set's is_authenitated if lookup fails
644 Additionally set's is_authenitated if lookup fails
645 present in database
645 present in database
646
646
647 :param auth_user: instance of user to set attributes
647 :param auth_user: instance of user to set attributes
648 :param user_id: user id to fetch by
648 :param user_id: user id to fetch by
649 :param api_key: api key to fetch by
649 :param api_key: api key to fetch by
650 :param username: username to fetch by
650 :param username: username to fetch by
651 """
651 """
652 if user_id is None and api_key is None and username is None:
652 if user_id is None and api_key is None and username is None:
653 raise Exception('You need to pass user_id, api_key or username')
653 raise Exception('You need to pass user_id, api_key or username')
654
654
655 log.debug(
655 log.debug(
656 'doing fill data based on: user_id:%s api_key:%s username:%s',
656 'doing fill data based on: user_id:%s api_key:%s username:%s',
657 user_id, api_key, username)
657 user_id, api_key, username)
658 try:
658 try:
659 dbuser = None
659 dbuser = None
660 if user_id:
660 if user_id:
661 dbuser = self.get(user_id)
661 dbuser = self.get(user_id)
662 elif api_key:
662 elif api_key:
663 dbuser = self.get_by_auth_token(api_key)
663 dbuser = self.get_by_auth_token(api_key)
664 elif username:
664 elif username:
665 dbuser = self.get_by_username(username)
665 dbuser = self.get_by_username(username)
666
666
667 if not dbuser:
667 if not dbuser:
668 log.warning(
668 log.warning(
669 'Unable to lookup user by id:%s api_key:%s username:%s',
669 'Unable to lookup user by id:%s api_key:%s username:%s',
670 user_id, api_key, username)
670 user_id, api_key, username)
671 return False
671 return False
672 if not dbuser.active:
672 if not dbuser.active:
673 log.debug('User `%s:%s` is inactive, skipping fill data',
673 log.debug('User `%s:%s` is inactive, skipping fill data',
674 username, user_id)
674 username, user_id)
675 return False
675 return False
676
676
677 log.debug('filling user:%s data', dbuser)
677 log.debug('filling user:%s data', dbuser)
678
678
679 # TODO: johbo: Think about this and find a clean solution
679 # TODO: johbo: Think about this and find a clean solution
680 user_data = dbuser.get_dict()
680 user_data = dbuser.get_dict()
681 user_data.update(dbuser.get_api_data(include_secrets=True))
681 user_data.update(dbuser.get_api_data(include_secrets=True))
682
682
683 for k, v in user_data.iteritems():
683 for k, v in user_data.iteritems():
684 # properties of auth user we dont update
684 # properties of auth user we dont update
685 if k not in ['auth_tokens', 'permissions']:
685 if k not in ['auth_tokens', 'permissions']:
686 setattr(auth_user, k, v)
686 setattr(auth_user, k, v)
687
687
688 # few extras
688 # few extras
689 setattr(auth_user, 'feed_token', dbuser.feed_token)
689 setattr(auth_user, 'feed_token', dbuser.feed_token)
690 except Exception:
690 except Exception:
691 log.error(traceback.format_exc())
691 log.error(traceback.format_exc())
692 auth_user.is_authenticated = False
692 auth_user.is_authenticated = False
693 return False
693 return False
694
694
695 return True
695 return True
696
696
697 def has_perm(self, user, perm):
697 def has_perm(self, user, perm):
698 perm = self._get_perm(perm)
698 perm = self._get_perm(perm)
699 user = self._get_user(user)
699 user = self._get_user(user)
700
700
701 return UserToPerm.query().filter(UserToPerm.user == user)\
701 return UserToPerm.query().filter(UserToPerm.user == user)\
702 .filter(UserToPerm.permission == perm).scalar() is not None
702 .filter(UserToPerm.permission == perm).scalar() is not None
703
703
704 def grant_perm(self, user, perm):
704 def grant_perm(self, user, perm):
705 """
705 """
706 Grant user global permissions
706 Grant user global permissions
707
707
708 :param user:
708 :param user:
709 :param perm:
709 :param perm:
710 """
710 """
711 user = self._get_user(user)
711 user = self._get_user(user)
712 perm = self._get_perm(perm)
712 perm = self._get_perm(perm)
713 # if this permission is already granted skip it
713 # if this permission is already granted skip it
714 _perm = UserToPerm.query()\
714 _perm = UserToPerm.query()\
715 .filter(UserToPerm.user == user)\
715 .filter(UserToPerm.user == user)\
716 .filter(UserToPerm.permission == perm)\
716 .filter(UserToPerm.permission == perm)\
717 .scalar()
717 .scalar()
718 if _perm:
718 if _perm:
719 return
719 return
720 new = UserToPerm()
720 new = UserToPerm()
721 new.user = user
721 new.user = user
722 new.permission = perm
722 new.permission = perm
723 self.sa.add(new)
723 self.sa.add(new)
724 return new
724 return new
725
725
726 def revoke_perm(self, user, perm):
726 def revoke_perm(self, user, perm):
727 """
727 """
728 Revoke users global permissions
728 Revoke users global permissions
729
729
730 :param user:
730 :param user:
731 :param perm:
731 :param perm:
732 """
732 """
733 user = self._get_user(user)
733 user = self._get_user(user)
734 perm = self._get_perm(perm)
734 perm = self._get_perm(perm)
735
735
736 obj = UserToPerm.query()\
736 obj = UserToPerm.query()\
737 .filter(UserToPerm.user == user)\
737 .filter(UserToPerm.user == user)\
738 .filter(UserToPerm.permission == perm)\
738 .filter(UserToPerm.permission == perm)\
739 .scalar()
739 .scalar()
740 if obj:
740 if obj:
741 self.sa.delete(obj)
741 self.sa.delete(obj)
742
742
743 def add_extra_email(self, user, email):
743 def add_extra_email(self, user, email):
744 """
744 """
745 Adds email address to UserEmailMap
745 Adds email address to UserEmailMap
746
746
747 :param user:
747 :param user:
748 :param email:
748 :param email:
749 """
749 """
750 from rhodecode.model import forms
750 from rhodecode.model import forms
751 form = forms.UserExtraEmailForm()()
751 form = forms.UserExtraEmailForm()()
752 data = form.to_python({'email': email})
752 data = form.to_python({'email': email})
753 user = self._get_user(user)
753 user = self._get_user(user)
754
754
755 obj = UserEmailMap()
755 obj = UserEmailMap()
756 obj.user = user
756 obj.user = user
757 obj.email = data['email']
757 obj.email = data['email']
758 self.sa.add(obj)
758 self.sa.add(obj)
759 return obj
759 return obj
760
760
761 def delete_extra_email(self, user, email_id):
761 def delete_extra_email(self, user, email_id):
762 """
762 """
763 Removes email address from UserEmailMap
763 Removes email address from UserEmailMap
764
764
765 :param user:
765 :param user:
766 :param email_id:
766 :param email_id:
767 """
767 """
768 user = self._get_user(user)
768 user = self._get_user(user)
769 obj = UserEmailMap.query().get(email_id)
769 obj = UserEmailMap.query().get(email_id)
770 if obj:
770 if obj and obj.user_id == user.user_id:
771 self.sa.delete(obj)
771 self.sa.delete(obj)
772
772
773 def parse_ip_range(self, ip_range):
773 def parse_ip_range(self, ip_range):
774 ip_list = []
774 ip_list = []
775 def make_unique(value):
775 def make_unique(value):
776 seen = []
776 seen = []
777 return [c for c in value if not (c in seen or seen.append(c))]
777 return [c for c in value if not (c in seen or seen.append(c))]
778
778
779 # firsts split by commas
779 # firsts split by commas
780 for ip_range in ip_range.split(','):
780 for ip_range in ip_range.split(','):
781 if not ip_range:
781 if not ip_range:
782 continue
782 continue
783 ip_range = ip_range.strip()
783 ip_range = ip_range.strip()
784 if '-' in ip_range:
784 if '-' in ip_range:
785 start_ip, end_ip = ip_range.split('-', 1)
785 start_ip, end_ip = ip_range.split('-', 1)
786 start_ip = ipaddress.ip_address(start_ip.strip())
786 start_ip = ipaddress.ip_address(start_ip.strip())
787 end_ip = ipaddress.ip_address(end_ip.strip())
787 end_ip = ipaddress.ip_address(end_ip.strip())
788 parsed_ip_range = []
788 parsed_ip_range = []
789
789
790 for index in xrange(int(start_ip), int(end_ip) + 1):
790 for index in xrange(int(start_ip), int(end_ip) + 1):
791 new_ip = ipaddress.ip_address(index)
791 new_ip = ipaddress.ip_address(index)
792 parsed_ip_range.append(str(new_ip))
792 parsed_ip_range.append(str(new_ip))
793 ip_list.extend(parsed_ip_range)
793 ip_list.extend(parsed_ip_range)
794 else:
794 else:
795 ip_list.append(ip_range)
795 ip_list.append(ip_range)
796
796
797 return make_unique(ip_list)
797 return make_unique(ip_list)
798
798
799 def add_extra_ip(self, user, ip, description=None):
799 def add_extra_ip(self, user, ip, description=None):
800 """
800 """
801 Adds ip address to UserIpMap
801 Adds ip address to UserIpMap
802
802
803 :param user:
803 :param user:
804 :param ip:
804 :param ip:
805 """
805 """
806 from rhodecode.model import forms
806 from rhodecode.model import forms
807 form = forms.UserExtraIpForm()()
807 form = forms.UserExtraIpForm()()
808 data = form.to_python({'ip': ip})
808 data = form.to_python({'ip': ip})
809 user = self._get_user(user)
809 user = self._get_user(user)
810
810
811 obj = UserIpMap()
811 obj = UserIpMap()
812 obj.user = user
812 obj.user = user
813 obj.ip_addr = data['ip']
813 obj.ip_addr = data['ip']
814 obj.description = description
814 obj.description = description
815 self.sa.add(obj)
815 self.sa.add(obj)
816 return obj
816 return obj
817
817
818 def delete_extra_ip(self, user, ip_id):
818 def delete_extra_ip(self, user, ip_id):
819 """
819 """
820 Removes ip address from UserIpMap
820 Removes ip address from UserIpMap
821
821
822 :param user:
822 :param user:
823 :param ip_id:
823 :param ip_id:
824 """
824 """
825 user = self._get_user(user)
825 user = self._get_user(user)
826 obj = UserIpMap.query().get(ip_id)
826 obj = UserIpMap.query().get(ip_id)
827 if obj:
827 if obj and obj.user_id == user.user_id:
828 self.sa.delete(obj)
828 self.sa.delete(obj)
829
829
830 def get_accounts_in_creation_order(self, current_user=None):
830 def get_accounts_in_creation_order(self, current_user=None):
831 """
831 """
832 Get accounts in order of creation for deactivation for license limits
832 Get accounts in order of creation for deactivation for license limits
833
833
834 pick currently logged in user, and append to the list in position 0
834 pick currently logged in user, and append to the list in position 0
835 pick all super-admins in order of creation date and add it to the list
835 pick all super-admins in order of creation date and add it to the list
836 pick all other accounts in order of creation and add it to the list.
836 pick all other accounts in order of creation and add it to the list.
837
837
838 Based on that list, the last accounts can be disabled as they are
838 Based on that list, the last accounts can be disabled as they are
839 created at the end and don't include any of the super admins as well
839 created at the end and don't include any of the super admins as well
840 as the current user.
840 as the current user.
841
841
842 :param current_user: optionally current user running this operation
842 :param current_user: optionally current user running this operation
843 """
843 """
844
844
845 if not current_user:
845 if not current_user:
846 current_user = get_current_rhodecode_user()
846 current_user = get_current_rhodecode_user()
847 active_super_admins = [
847 active_super_admins = [
848 x.user_id for x in User.query()
848 x.user_id for x in User.query()
849 .filter(User.user_id != current_user.user_id)
849 .filter(User.user_id != current_user.user_id)
850 .filter(User.active == true())
850 .filter(User.active == true())
851 .filter(User.admin == true())
851 .filter(User.admin == true())
852 .order_by(User.created_on.asc())]
852 .order_by(User.created_on.asc())]
853
853
854 active_regular_users = [
854 active_regular_users = [
855 x.user_id for x in User.query()
855 x.user_id for x in User.query()
856 .filter(User.user_id != current_user.user_id)
856 .filter(User.user_id != current_user.user_id)
857 .filter(User.active == true())
857 .filter(User.active == true())
858 .filter(User.admin == false())
858 .filter(User.admin == false())
859 .order_by(User.created_on.asc())]
859 .order_by(User.created_on.asc())]
860
860
861 list_of_accounts = [current_user.user_id]
861 list_of_accounts = [current_user.user_id]
862 list_of_accounts += active_super_admins
862 list_of_accounts += active_super_admins
863 list_of_accounts += active_regular_users
863 list_of_accounts += active_regular_users
864
864
865 return list_of_accounts
865 return list_of_accounts
866
866
867 def deactivate_last_users(self, expected_users):
867 def deactivate_last_users(self, expected_users):
868 """
868 """
869 Deactivate accounts that are over the license limits.
869 Deactivate accounts that are over the license limits.
870 Algorithm of which accounts to disabled is based on the formula:
870 Algorithm of which accounts to disabled is based on the formula:
871
871
872 Get current user, then super admins in creation order, then regular
872 Get current user, then super admins in creation order, then regular
873 active users in creation order.
873 active users in creation order.
874
874
875 Using that list we mark all accounts from the end of it as inactive.
875 Using that list we mark all accounts from the end of it as inactive.
876 This way we block only latest created accounts.
876 This way we block only latest created accounts.
877
877
878 :param expected_users: list of users in special order, we deactivate
878 :param expected_users: list of users in special order, we deactivate
879 the end N ammoun of users from that list
879 the end N ammoun of users from that list
880 """
880 """
881
881
882 list_of_accounts = self.get_accounts_in_creation_order()
882 list_of_accounts = self.get_accounts_in_creation_order()
883
883
884 for acc_id in list_of_accounts[expected_users + 1:]:
884 for acc_id in list_of_accounts[expected_users + 1:]:
885 user = User.get(acc_id)
885 user = User.get(acc_id)
886 log.info('Deactivating account %s for license unlock', user)
886 log.info('Deactivating account %s for license unlock', user)
887 user.active = False
887 user.active = False
888 Session().add(user)
888 Session().add(user)
889 Session().commit()
889 Session().commit()
890
890
891 return
891 return
892
892
893 def get_user_log(self, user, filter_term):
893 def get_user_log(self, user, filter_term):
894 user_log = UserLog.query()\
894 user_log = UserLog.query()\
895 .filter(or_(UserLog.user_id == user.user_id,
895 .filter(or_(UserLog.user_id == user.user_id,
896 UserLog.username == user.username))\
896 UserLog.username == user.username))\
897 .options(joinedload(UserLog.user))\
897 .options(joinedload(UserLog.user))\
898 .options(joinedload(UserLog.repository))\
898 .options(joinedload(UserLog.repository))\
899 .order_by(UserLog.action_date.desc())
899 .order_by(UserLog.action_date.desc())
900
900
901 user_log = user_log_filter(user_log, filter_term)
901 user_log = user_log_filter(user_log, filter_term)
902 return user_log
902 return user_log
General Comments 0
You need to be logged in to leave comments. Login now