##// END OF EJS Templates
emails: fixed password reset confirmation that was broken because of undefied variable.
marcink -
r531:91bf0444 default
parent child Browse files
Show More
@@ -1,340 +1,343 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
3 # Copyright (C) 2016-2016 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 datetime
21 import datetime
22 import formencode
22 import formencode
23 import logging
23 import logging
24 import urlparse
24 import urlparse
25
25
26 from pylons import url
26 from pylons import url
27 from pyramid.httpexceptions import HTTPFound
27 from pyramid.httpexceptions import HTTPFound
28 from pyramid.view import view_config
28 from pyramid.view import view_config
29 from recaptcha.client.captcha import submit
29 from recaptcha.client.captcha import submit
30
30
31 from rhodecode.authentication.base import authenticate, HTTP_TYPE
31 from rhodecode.authentication.base import authenticate, HTTP_TYPE
32 from rhodecode.events import UserRegistered
32 from rhodecode.events import UserRegistered
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
34 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
35 from rhodecode.lib.base import get_ip_addr
35 from rhodecode.lib.base import get_ip_addr
36 from rhodecode.lib.exceptions import UserCreationError
36 from rhodecode.lib.exceptions import UserCreationError
37 from rhodecode.lib.utils2 import safe_str
37 from rhodecode.lib.utils2 import safe_str
38 from rhodecode.model.db import User
38 from rhodecode.model.db import User
39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 from rhodecode.model.login_session import LoginSession
40 from rhodecode.model.login_session import LoginSession
41 from rhodecode.model.meta import Session
41 from rhodecode.model.meta import Session
42 from rhodecode.model.settings import SettingsModel
42 from rhodecode.model.settings import SettingsModel
43 from rhodecode.model.user import UserModel
43 from rhodecode.model.user import UserModel
44 from rhodecode.translation import _
44 from rhodecode.translation import _
45
45
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 def _store_user_in_session(session, username, remember=False):
50 def _store_user_in_session(session, username, remember=False):
51 user = User.get_by_username(username, case_insensitive=True)
51 user = User.get_by_username(username, case_insensitive=True)
52 auth_user = AuthUser(user.user_id)
52 auth_user = AuthUser(user.user_id)
53 auth_user.set_authenticated()
53 auth_user.set_authenticated()
54 cs = auth_user.get_cookie_store()
54 cs = auth_user.get_cookie_store()
55 session['rhodecode_user'] = cs
55 session['rhodecode_user'] = cs
56 user.update_lastlogin()
56 user.update_lastlogin()
57 Session().commit()
57 Session().commit()
58
58
59 # If they want to be remembered, update the cookie
59 # If they want to be remembered, update the cookie
60 if remember:
60 if remember:
61 _year = (datetime.datetime.now() +
61 _year = (datetime.datetime.now() +
62 datetime.timedelta(seconds=60 * 60 * 24 * 365))
62 datetime.timedelta(seconds=60 * 60 * 24 * 365))
63 session._set_cookie_expires(_year)
63 session._set_cookie_expires(_year)
64
64
65 session.save()
65 session.save()
66
66
67 log.info('user %s is now authenticated and stored in '
67 log.info('user %s is now authenticated and stored in '
68 'session, session attrs %s', username, cs)
68 'session, session attrs %s', username, cs)
69
69
70 # dumps session attrs back to cookie
70 # dumps session attrs back to cookie
71 session._update_cookie_out()
71 session._update_cookie_out()
72 # we set new cookie
72 # we set new cookie
73 headers = None
73 headers = None
74 if session.request['set_cookie']:
74 if session.request['set_cookie']:
75 # send set-cookie headers back to response to update cookie
75 # send set-cookie headers back to response to update cookie
76 headers = [('Set-Cookie', session.request['cookie_out'])]
76 headers = [('Set-Cookie', session.request['cookie_out'])]
77 return headers
77 return headers
78
78
79
79
80 def get_came_from(request):
80 def get_came_from(request):
81 came_from = safe_str(request.GET.get('came_from', ''))
81 came_from = safe_str(request.GET.get('came_from', ''))
82 parsed = urlparse.urlparse(came_from)
82 parsed = urlparse.urlparse(came_from)
83 allowed_schemes = ['http', 'https']
83 allowed_schemes = ['http', 'https']
84 if parsed.scheme and parsed.scheme not in allowed_schemes:
84 if parsed.scheme and parsed.scheme not in allowed_schemes:
85 log.error('Suspicious URL scheme detected %s for url %s' %
85 log.error('Suspicious URL scheme detected %s for url %s' %
86 (parsed.scheme, parsed))
86 (parsed.scheme, parsed))
87 came_from = url('home')
87 came_from = url('home')
88 elif parsed.netloc and request.host != parsed.netloc:
88 elif parsed.netloc and request.host != parsed.netloc:
89 log.error('Suspicious NETLOC detected %s for url %s server url '
89 log.error('Suspicious NETLOC detected %s for url %s server url '
90 'is: %s' % (parsed.netloc, parsed, request.host))
90 'is: %s' % (parsed.netloc, parsed, request.host))
91 came_from = url('home')
91 came_from = url('home')
92 elif any(bad_str in parsed.path for bad_str in ('\r', '\n')):
92 elif any(bad_str in parsed.path for bad_str in ('\r', '\n')):
93 log.error('Header injection detected `%s` for url %s server url ' %
93 log.error('Header injection detected `%s` for url %s server url ' %
94 (parsed.path, parsed))
94 (parsed.path, parsed))
95 came_from = url('home')
95 came_from = url('home')
96
96
97 return came_from or url('home')
97 return came_from or url('home')
98
98
99
99
100 class LoginView(object):
100 class LoginView(object):
101
101
102 def __init__(self, context, request):
102 def __init__(self, context, request):
103 self.request = request
103 self.request = request
104 self.context = context
104 self.context = context
105 self.session = request.session
105 self.session = request.session
106 self._rhodecode_user = request.user
106 self._rhodecode_user = request.user
107
107
108 def _get_template_context(self):
108 def _get_template_context(self):
109 return {
109 return {
110 'came_from': get_came_from(self.request),
110 'came_from': get_came_from(self.request),
111 'defaults': {},
111 'defaults': {},
112 'errors': {},
112 'errors': {},
113 }
113 }
114
114
115 @view_config(
115 @view_config(
116 route_name='login', request_method='GET',
116 route_name='login', request_method='GET',
117 renderer='rhodecode:templates/login.html')
117 renderer='rhodecode:templates/login.html')
118 def login(self):
118 def login(self):
119 came_from = get_came_from(self.request)
119 came_from = get_came_from(self.request)
120 user = self.request.user
120 user = self.request.user
121
121
122 # redirect if already logged in
122 # redirect if already logged in
123 if user.is_authenticated and not user.is_default and user.ip_allowed:
123 if user.is_authenticated and not user.is_default and user.ip_allowed:
124 raise HTTPFound(came_from)
124 raise HTTPFound(came_from)
125
125
126 # check if we use headers plugin, and try to login using it.
126 # check if we use headers plugin, and try to login using it.
127 try:
127 try:
128 log.debug('Running PRE-AUTH for headers based authentication')
128 log.debug('Running PRE-AUTH for headers based authentication')
129 auth_info = authenticate(
129 auth_info = authenticate(
130 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
130 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
131 if auth_info:
131 if auth_info:
132 headers = _store_user_in_session(
132 headers = _store_user_in_session(
133 self.session, auth_info.get('username'))
133 self.session, auth_info.get('username'))
134 raise HTTPFound(came_from, headers=headers)
134 raise HTTPFound(came_from, headers=headers)
135 except UserCreationError as e:
135 except UserCreationError as e:
136 log.error(e)
136 log.error(e)
137 self.session.flash(e, queue='error')
137 self.session.flash(e, queue='error')
138
138
139 return self._get_template_context()
139 return self._get_template_context()
140
140
141 @view_config(
141 @view_config(
142 route_name='login', request_method='POST',
142 route_name='login', request_method='POST',
143 renderer='rhodecode:templates/login.html')
143 renderer='rhodecode:templates/login.html')
144 def login_post(self):
144 def login_post(self):
145 came_from = get_came_from(self.request)
145 came_from = get_came_from(self.request)
146 session = self.request.session
146 session = self.request.session
147 login_form = LoginForm()()
147 login_form = LoginForm()()
148
148
149 try:
149 try:
150 session.invalidate()
150 session.invalidate()
151 form_result = login_form.to_python(self.request.params)
151 form_result = login_form.to_python(self.request.params)
152 # form checks for username/password, now we're authenticated
152 # form checks for username/password, now we're authenticated
153 headers = _store_user_in_session(
153 headers = _store_user_in_session(
154 self.session,
154 self.session,
155 username=form_result['username'],
155 username=form_result['username'],
156 remember=form_result['remember'])
156 remember=form_result['remember'])
157 log.debug('Redirecting to "%s" after login.', came_from)
157 log.debug('Redirecting to "%s" after login.', came_from)
158 raise HTTPFound(came_from, headers=headers)
158 raise HTTPFound(came_from, headers=headers)
159 except formencode.Invalid as errors:
159 except formencode.Invalid as errors:
160 defaults = errors.value
160 defaults = errors.value
161 # remove password from filling in form again
161 # remove password from filling in form again
162 del defaults['password']
162 del defaults['password']
163 render_ctx = self._get_template_context()
163 render_ctx = self._get_template_context()
164 render_ctx.update({
164 render_ctx.update({
165 'errors': errors.error_dict,
165 'errors': errors.error_dict,
166 'defaults': defaults,
166 'defaults': defaults,
167 })
167 })
168 return render_ctx
168 return render_ctx
169
169
170 except UserCreationError as e:
170 except UserCreationError as e:
171 # headers auth or other auth functions that create users on
171 # headers auth or other auth functions that create users on
172 # the fly can throw this exception signaling that there's issue
172 # the fly can throw this exception signaling that there's issue
173 # with user creation, explanation should be provided in
173 # with user creation, explanation should be provided in
174 # Exception itself
174 # Exception itself
175 session.flash(e, queue='error')
175 session.flash(e, queue='error')
176 return self._get_template_context()
176 return self._get_template_context()
177
177
178 @CSRFRequired()
178 @CSRFRequired()
179 @view_config(route_name='logout', request_method='POST')
179 @view_config(route_name='logout', request_method='POST')
180 def logout(self):
180 def logout(self):
181 LoginSession().destroy_user_session()
181 LoginSession().destroy_user_session()
182 return HTTPFound(url('home'))
182 return HTTPFound(url('home'))
183
183
184 @HasPermissionAnyDecorator(
184 @HasPermissionAnyDecorator(
185 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
185 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
186 @view_config(
186 @view_config(
187 route_name='register', request_method='GET',
187 route_name='register', request_method='GET',
188 renderer='rhodecode:templates/register.html',)
188 renderer='rhodecode:templates/register.html',)
189 def register(self, defaults=None, errors=None):
189 def register(self, defaults=None, errors=None):
190 defaults = defaults or {}
190 defaults = defaults or {}
191 errors = errors or {}
191 errors = errors or {}
192
192
193 settings = SettingsModel().get_all_settings()
193 settings = SettingsModel().get_all_settings()
194 captcha_public_key = settings.get('rhodecode_captcha_public_key')
194 captcha_public_key = settings.get('rhodecode_captcha_public_key')
195 captcha_private_key = settings.get('rhodecode_captcha_private_key')
195 captcha_private_key = settings.get('rhodecode_captcha_private_key')
196 captcha_active = bool(captcha_private_key)
196 captcha_active = bool(captcha_private_key)
197 register_message = settings.get('rhodecode_register_message') or ''
197 register_message = settings.get('rhodecode_register_message') or ''
198 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
198 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
199 .AuthUser.permissions['global']
199 .AuthUser.permissions['global']
200
200
201 render_ctx = self._get_template_context()
201 render_ctx = self._get_template_context()
202 render_ctx.update({
202 render_ctx.update({
203 'defaults': defaults,
203 'defaults': defaults,
204 'errors': errors,
204 'errors': errors,
205 'auto_active': auto_active,
205 'auto_active': auto_active,
206 'captcha_active': captcha_active,
206 'captcha_active': captcha_active,
207 'captcha_public_key': captcha_public_key,
207 'captcha_public_key': captcha_public_key,
208 'register_message': register_message,
208 'register_message': register_message,
209 })
209 })
210 return render_ctx
210 return render_ctx
211
211
212 @HasPermissionAnyDecorator(
212 @HasPermissionAnyDecorator(
213 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
213 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
214 @view_config(
214 @view_config(
215 route_name='register', request_method='POST',
215 route_name='register', request_method='POST',
216 renderer='rhodecode:templates/register.html')
216 renderer='rhodecode:templates/register.html')
217 def register_post(self):
217 def register_post(self):
218 captcha_private_key = SettingsModel().get_setting_by_name(
218 captcha_private_key = SettingsModel().get_setting_by_name(
219 'rhodecode_captcha_private_key')
219 'rhodecode_captcha_private_key')
220 captcha_active = bool(captcha_private_key)
220 captcha_active = bool(captcha_private_key)
221 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
221 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
222 .AuthUser.permissions['global']
222 .AuthUser.permissions['global']
223
223
224 register_form = RegisterForm()()
224 register_form = RegisterForm()()
225 try:
225 try:
226 form_result = register_form.to_python(self.request.params)
226 form_result = register_form.to_python(self.request.params)
227 form_result['active'] = auto_active
227 form_result['active'] = auto_active
228
228
229 if captcha_active:
229 if captcha_active:
230 response = submit(
230 response = submit(
231 self.request.params.get('recaptcha_challenge_field'),
231 self.request.params.get('recaptcha_challenge_field'),
232 self.request.params.get('recaptcha_response_field'),
232 self.request.params.get('recaptcha_response_field'),
233 private_key=captcha_private_key,
233 private_key=captcha_private_key,
234 remoteip=get_ip_addr(self.request.environ))
234 remoteip=get_ip_addr(self.request.environ))
235 if captcha_active and not response.is_valid:
235 if captcha_active and not response.is_valid:
236 _value = form_result
236 _value = form_result
237 _msg = _('bad captcha')
237 _msg = _('bad captcha')
238 error_dict = {'recaptcha_field': _msg}
238 error_dict = {'recaptcha_field': _msg}
239 raise formencode.Invalid(_msg, _value, None,
239 raise formencode.Invalid(_msg, _value, None,
240 error_dict=error_dict)
240 error_dict=error_dict)
241
241
242 new_user = UserModel().create_registration(form_result)
242 new_user = UserModel().create_registration(form_result)
243 event = UserRegistered(user=new_user, session=self.session)
243 event = UserRegistered(user=new_user, session=self.session)
244 self.request.registry.notify(event)
244 self.request.registry.notify(event)
245 self.session.flash(
245 self.session.flash(
246 _('You have successfully registered with RhodeCode'),
246 _('You have successfully registered with RhodeCode'),
247 queue='success')
247 queue='success')
248 Session().commit()
248 Session().commit()
249
249
250 redirect_ro = self.request.route_path('login')
250 redirect_ro = self.request.route_path('login')
251 raise HTTPFound(redirect_ro)
251 raise HTTPFound(redirect_ro)
252
252
253 except formencode.Invalid as errors:
253 except formencode.Invalid as errors:
254 del errors.value['password']
254 del errors.value['password']
255 del errors.value['password_confirmation']
255 del errors.value['password_confirmation']
256 return self.register(
256 return self.register(
257 defaults=errors.value, errors=errors.error_dict)
257 defaults=errors.value, errors=errors.error_dict)
258
258
259 except UserCreationError as e:
259 except UserCreationError as e:
260 # container auth or other auth functions that create users on
260 # container auth or other auth functions that create users on
261 # the fly can throw this exception signaling that there's issue
261 # the fly can throw this exception signaling that there's issue
262 # with user creation, explanation should be provided in
262 # with user creation, explanation should be provided in
263 # Exception itself
263 # Exception itself
264 self.session.flash(e, queue='error')
264 self.session.flash(e, queue='error')
265 return self.register()
265 return self.register()
266
266
267 @view_config(
267 @view_config(
268 route_name='reset_password', request_method=('GET', 'POST'),
268 route_name='reset_password', request_method=('GET', 'POST'),
269 renderer='rhodecode:templates/password_reset.html')
269 renderer='rhodecode:templates/password_reset.html')
270 def password_reset(self):
270 def password_reset(self):
271 settings = SettingsModel().get_all_settings()
271 settings = SettingsModel().get_all_settings()
272 captcha_private_key = settings.get('rhodecode_captcha_private_key')
272 captcha_private_key = settings.get('rhodecode_captcha_private_key')
273 captcha_active = bool(captcha_private_key)
273 captcha_active = bool(captcha_private_key)
274 captcha_public_key = settings.get('rhodecode_captcha_public_key')
274 captcha_public_key = settings.get('rhodecode_captcha_public_key')
275
275
276 render_ctx = {
276 render_ctx = {
277 'captcha_active': captcha_active,
277 'captcha_active': captcha_active,
278 'captcha_public_key': captcha_public_key,
278 'captcha_public_key': captcha_public_key,
279 'defaults': {},
279 'defaults': {},
280 'errors': {},
280 'errors': {},
281 }
281 }
282
282
283 if self.request.POST:
283 if self.request.POST:
284 password_reset_form = PasswordResetForm()()
284 password_reset_form = PasswordResetForm()()
285 try:
285 try:
286 form_result = password_reset_form.to_python(
286 form_result = password_reset_form.to_python(
287 self.request.params)
287 self.request.params)
288 if captcha_active:
288 if captcha_active:
289 response = submit(
289 response = submit(
290 self.request.params.get('recaptcha_challenge_field'),
290 self.request.params.get('recaptcha_challenge_field'),
291 self.request.params.get('recaptcha_response_field'),
291 self.request.params.get('recaptcha_response_field'),
292 private_key=captcha_private_key,
292 private_key=captcha_private_key,
293 remoteip=get_ip_addr(self.request.environ))
293 remoteip=get_ip_addr(self.request.environ))
294 if captcha_active and not response.is_valid:
294 if captcha_active and not response.is_valid:
295 _value = form_result
295 _value = form_result
296 _msg = _('bad captcha')
296 _msg = _('bad captcha')
297 error_dict = {'recaptcha_field': _msg}
297 error_dict = {'recaptcha_field': _msg}
298 raise formencode.Invalid(_msg, _value, None,
298 raise formencode.Invalid(_msg, _value, None,
299 error_dict=error_dict)
299 error_dict=error_dict)
300
300
301 # Generate reset URL and send mail.
301 # Generate reset URL and send mail.
302 user_email = form_result['email']
302 user_email = form_result['email']
303 user = User.get_by_email(user_email)
303 user = User.get_by_email(user_email)
304 password_reset_url = self.request.route_url(
304 password_reset_url = self.request.route_url(
305 'reset_password_confirmation',
305 'reset_password_confirmation',
306 _query={'key': user.api_key})
306 _query={'key': user.api_key})
307 UserModel().reset_password_link(
307 UserModel().reset_password_link(
308 form_result, password_reset_url)
308 form_result, password_reset_url)
309
309
310 # Display success message and redirect.
310 # Display success message and redirect.
311 self.session.flash(
311 self.session.flash(
312 _('Your password reset link was sent'),
312 _('Your password reset link was sent'),
313 queue='success')
313 queue='success')
314 return HTTPFound(self.request.route_path('login'))
314 return HTTPFound(self.request.route_path('login'))
315
315
316 except formencode.Invalid as errors:
316 except formencode.Invalid as errors:
317 render_ctx.update({
317 render_ctx.update({
318 'defaults': errors.value,
318 'defaults': errors.value,
319 'errors': errors.error_dict,
319 'errors': errors.error_dict,
320 })
320 })
321
321
322 return render_ctx
322 return render_ctx
323
323
324 @view_config(route_name='reset_password_confirmation',
324 @view_config(route_name='reset_password_confirmation',
325 request_method='GET')
325 request_method='GET')
326 def password_reset_confirmation(self):
326 def password_reset_confirmation(self):
327 if self.request.GET and self.request.GET.get('key'):
327 if self.request.GET and self.request.GET.get('key'):
328 try:
328 try:
329 user = User.get_by_auth_token(self.request.GET.get('key'))
329 user = User.get_by_auth_token(self.request.GET.get('key'))
330 password_reset_url = self.request.route_url(
331 'reset_password_confirmation',
332 _query={'key': user.api_key})
330 data = {'email': user.email}
333 data = {'email': user.email}
331 UserModel().reset_password(data)
334 UserModel().reset_password(data, password_reset_url)
332 self.session.flash(
335 self.session.flash(
333 _('Your password reset was successful, '
336 _('Your password reset was successful, '
334 'a new password has been sent to your email'),
337 'a new password has been sent to your email'),
335 queue='success')
338 queue='success')
336 except Exception as e:
339 except Exception as e:
337 log.error(e)
340 log.error(e)
338 return HTTPFound(self.request.route_path('reset_password'))
341 return HTTPFound(self.request.route_path('reset_password'))
339
342
340 return HTTPFound(self.request.route_path('login'))
343 return HTTPFound(self.request.route_path('login'))
@@ -1,838 +1,839 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 from sqlalchemy.sql.expression import true, false
33 from sqlalchemy.sql.expression import true, false
34
34
35 from rhodecode import events
35 from rhodecode import events
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)
38 AttributeDict)
39 from rhodecode.lib.caching_query import FromCache
39 from rhodecode.lib.caching_query import FromCache
40 from rhodecode.model import BaseModel
40 from rhodecode.model import BaseModel
41 from rhodecode.model.auth_token import AuthTokenModel
41 from rhodecode.model.auth_token import AuthTokenModel
42 from rhodecode.model.db import (
42 from rhodecode.model.db import (
43 User, UserToPerm, UserEmailMap, UserIpMap)
43 User, UserToPerm, UserEmailMap, UserIpMap)
44 from rhodecode.lib.exceptions import (
44 from rhodecode.lib.exceptions import (
45 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
45 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
46 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
46 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.model.repo_group import RepoGroupModel
48 from rhodecode.model.repo_group import RepoGroupModel
49
49
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 class UserModel(BaseModel):
54 class UserModel(BaseModel):
55 cls = User
55 cls = User
56
56
57 def get(self, user_id, cache=False):
57 def get(self, user_id, cache=False):
58 user = self.sa.query(User)
58 user = self.sa.query(User)
59 if cache:
59 if cache:
60 user = user.options(FromCache("sql_cache_short",
60 user = user.options(FromCache("sql_cache_short",
61 "get_user_%s" % user_id))
61 "get_user_%s" % user_id))
62 return user.get(user_id)
62 return user.get(user_id)
63
63
64 def get_user(self, user):
64 def get_user(self, user):
65 return self._get_user(user)
65 return self._get_user(user)
66
66
67 def get_by_username(self, username, cache=False, case_insensitive=False):
67 def get_by_username(self, username, cache=False, case_insensitive=False):
68
68
69 if case_insensitive:
69 if case_insensitive:
70 user = self.sa.query(User).filter(User.username.ilike(username))
70 user = self.sa.query(User).filter(User.username.ilike(username))
71 else:
71 else:
72 user = self.sa.query(User)\
72 user = self.sa.query(User)\
73 .filter(User.username == username)
73 .filter(User.username == username)
74 if cache:
74 if cache:
75 user = user.options(FromCache("sql_cache_short",
75 user = user.options(FromCache("sql_cache_short",
76 "get_user_%s" % username))
76 "get_user_%s" % username))
77 return user.scalar()
77 return user.scalar()
78
78
79 def get_by_email(self, email, cache=False, case_insensitive=False):
79 def get_by_email(self, email, cache=False, case_insensitive=False):
80 return User.get_by_email(email, case_insensitive, cache)
80 return User.get_by_email(email, case_insensitive, cache)
81
81
82 def get_by_auth_token(self, auth_token, cache=False):
82 def get_by_auth_token(self, auth_token, cache=False):
83 return User.get_by_auth_token(auth_token, cache)
83 return User.get_by_auth_token(auth_token, cache)
84
84
85 def get_active_user_count(self, cache=False):
85 def get_active_user_count(self, cache=False):
86 return User.query().filter(
86 return User.query().filter(
87 User.active == True).filter(
87 User.active == True).filter(
88 User.username != User.DEFAULT_USER).count()
88 User.username != User.DEFAULT_USER).count()
89
89
90 def create(self, form_data, cur_user=None):
90 def create(self, form_data, cur_user=None):
91 if not cur_user:
91 if not cur_user:
92 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
92 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
93
93
94 user_data = {
94 user_data = {
95 'username': form_data['username'],
95 'username': form_data['username'],
96 'password': form_data['password'],
96 'password': form_data['password'],
97 'email': form_data['email'],
97 'email': form_data['email'],
98 'firstname': form_data['firstname'],
98 'firstname': form_data['firstname'],
99 'lastname': form_data['lastname'],
99 'lastname': form_data['lastname'],
100 'active': form_data['active'],
100 'active': form_data['active'],
101 'extern_type': form_data['extern_type'],
101 'extern_type': form_data['extern_type'],
102 'extern_name': form_data['extern_name'],
102 'extern_name': form_data['extern_name'],
103 'admin': False,
103 'admin': False,
104 'cur_user': cur_user
104 'cur_user': cur_user
105 }
105 }
106
106
107 try:
107 try:
108 if form_data.get('create_repo_group'):
108 if form_data.get('create_repo_group'):
109 user_data['create_repo_group'] = True
109 user_data['create_repo_group'] = True
110 if form_data.get('password_change'):
110 if form_data.get('password_change'):
111 user_data['force_password_change'] = True
111 user_data['force_password_change'] = True
112
112
113 return UserModel().create_or_update(**user_data)
113 return UserModel().create_or_update(**user_data)
114 except Exception:
114 except Exception:
115 log.error(traceback.format_exc())
115 log.error(traceback.format_exc())
116 raise
116 raise
117
117
118 def update_user(self, user, skip_attrs=None, **kwargs):
118 def update_user(self, user, skip_attrs=None, **kwargs):
119 from rhodecode.lib.auth import get_crypt_password
119 from rhodecode.lib.auth import get_crypt_password
120
120
121 user = self._get_user(user)
121 user = self._get_user(user)
122 if user.username == User.DEFAULT_USER:
122 if user.username == User.DEFAULT_USER:
123 raise DefaultUserException(
123 raise DefaultUserException(
124 _("You can't Edit this user since it's"
124 _("You can't Edit this user since it's"
125 " crucial for entire application"))
125 " crucial for entire application"))
126
126
127 # first store only defaults
127 # first store only defaults
128 user_attrs = {
128 user_attrs = {
129 'updating_user_id': user.user_id,
129 'updating_user_id': user.user_id,
130 'username': user.username,
130 'username': user.username,
131 'password': user.password,
131 'password': user.password,
132 'email': user.email,
132 'email': user.email,
133 'firstname': user.name,
133 'firstname': user.name,
134 'lastname': user.lastname,
134 'lastname': user.lastname,
135 'active': user.active,
135 'active': user.active,
136 'admin': user.admin,
136 'admin': user.admin,
137 'extern_name': user.extern_name,
137 'extern_name': user.extern_name,
138 'extern_type': user.extern_type,
138 'extern_type': user.extern_type,
139 'language': user.user_data.get('language')
139 'language': user.user_data.get('language')
140 }
140 }
141
141
142 # in case there's new_password, that comes from form, use it to
142 # in case there's new_password, that comes from form, use it to
143 # store password
143 # store password
144 if kwargs.get('new_password'):
144 if kwargs.get('new_password'):
145 kwargs['password'] = kwargs['new_password']
145 kwargs['password'] = kwargs['new_password']
146
146
147 # cleanups, my_account password change form
147 # cleanups, my_account password change form
148 kwargs.pop('current_password', None)
148 kwargs.pop('current_password', None)
149 kwargs.pop('new_password', None)
149 kwargs.pop('new_password', None)
150 kwargs.pop('new_password_confirmation', None)
150 kwargs.pop('new_password_confirmation', None)
151
151
152 # cleanups, user edit password change form
152 # cleanups, user edit password change form
153 kwargs.pop('password_confirmation', None)
153 kwargs.pop('password_confirmation', None)
154 kwargs.pop('password_change', None)
154 kwargs.pop('password_change', None)
155
155
156 # create repo group on user creation
156 # create repo group on user creation
157 kwargs.pop('create_repo_group', None)
157 kwargs.pop('create_repo_group', None)
158
158
159 # legacy forms send name, which is the firstname
159 # legacy forms send name, which is the firstname
160 firstname = kwargs.pop('name', None)
160 firstname = kwargs.pop('name', None)
161 if firstname:
161 if firstname:
162 kwargs['firstname'] = firstname
162 kwargs['firstname'] = firstname
163
163
164 for k, v in kwargs.items():
164 for k, v in kwargs.items():
165 # skip if we don't want to update this
165 # skip if we don't want to update this
166 if skip_attrs and k in skip_attrs:
166 if skip_attrs and k in skip_attrs:
167 continue
167 continue
168
168
169 user_attrs[k] = v
169 user_attrs[k] = v
170
170
171 try:
171 try:
172 return self.create_or_update(**user_attrs)
172 return self.create_or_update(**user_attrs)
173 except Exception:
173 except Exception:
174 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
175 raise
175 raise
176
176
177 def create_or_update(
177 def create_or_update(
178 self, username, password, email, firstname='', lastname='',
178 self, username, password, email, firstname='', lastname='',
179 active=True, admin=False, extern_type=None, extern_name=None,
179 active=True, admin=False, extern_type=None, extern_name=None,
180 cur_user=None, plugin=None, force_password_change=False,
180 cur_user=None, plugin=None, force_password_change=False,
181 allow_to_create_user=True, create_repo_group=False,
181 allow_to_create_user=True, create_repo_group=False,
182 updating_user_id=None, language=None, strict_creation_check=True):
182 updating_user_id=None, language=None, strict_creation_check=True):
183 """
183 """
184 Creates a new instance if not found, or updates current one
184 Creates a new instance if not found, or updates current one
185
185
186 :param username:
186 :param username:
187 :param password:
187 :param password:
188 :param email:
188 :param email:
189 :param firstname:
189 :param firstname:
190 :param lastname:
190 :param lastname:
191 :param active:
191 :param active:
192 :param admin:
192 :param admin:
193 :param extern_type:
193 :param extern_type:
194 :param extern_name:
194 :param extern_name:
195 :param cur_user:
195 :param cur_user:
196 :param plugin: optional plugin this method was called from
196 :param plugin: optional plugin this method was called from
197 :param force_password_change: toggles new or existing user flag
197 :param force_password_change: toggles new or existing user flag
198 for password change
198 for password change
199 :param allow_to_create_user: Defines if the method can actually create
199 :param allow_to_create_user: Defines if the method can actually create
200 new users
200 new users
201 :param create_repo_group: Defines if the method should also
201 :param create_repo_group: Defines if the method should also
202 create an repo group with user name, and owner
202 create an repo group with user name, and owner
203 :param updating_user_id: if we set it up this is the user we want to
203 :param updating_user_id: if we set it up this is the user we want to
204 update this allows to editing username.
204 update this allows to editing username.
205 :param language: language of user from interface.
205 :param language: language of user from interface.
206
206
207 :returns: new User object with injected `is_new_user` attribute.
207 :returns: new User object with injected `is_new_user` attribute.
208 """
208 """
209 if not cur_user:
209 if not cur_user:
210 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
210 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
211
211
212 from rhodecode.lib.auth import (
212 from rhodecode.lib.auth import (
213 get_crypt_password, check_password, generate_auth_token)
213 get_crypt_password, check_password, generate_auth_token)
214 from rhodecode.lib.hooks_base import (
214 from rhodecode.lib.hooks_base import (
215 log_create_user, check_allowed_create_user)
215 log_create_user, check_allowed_create_user)
216
216
217 def _password_change(new_user, password):
217 def _password_change(new_user, password):
218 # empty password
218 # empty password
219 if not new_user.password:
219 if not new_user.password:
220 return False
220 return False
221
221
222 # password check is only needed for RhodeCode internal auth calls
222 # password check is only needed for RhodeCode internal auth calls
223 # in case it's a plugin we don't care
223 # in case it's a plugin we don't care
224 if not plugin:
224 if not plugin:
225
225
226 # first check if we gave crypted password back, and if it matches
226 # first check if we gave crypted password back, and if it matches
227 # it's not password change
227 # it's not password change
228 if new_user.password == password:
228 if new_user.password == password:
229 return False
229 return False
230
230
231 password_match = check_password(password, new_user.password)
231 password_match = check_password(password, new_user.password)
232 if not password_match:
232 if not password_match:
233 return True
233 return True
234
234
235 return False
235 return False
236
236
237 user_data = {
237 user_data = {
238 'username': username,
238 'username': username,
239 'password': password,
239 'password': password,
240 'email': email,
240 'email': email,
241 'firstname': firstname,
241 'firstname': firstname,
242 'lastname': lastname,
242 'lastname': lastname,
243 'active': active,
243 'active': active,
244 'admin': admin
244 'admin': admin
245 }
245 }
246
246
247 if updating_user_id:
247 if updating_user_id:
248 log.debug('Checking for existing account in RhodeCode '
248 log.debug('Checking for existing account in RhodeCode '
249 'database with user_id `%s` ' % (updating_user_id,))
249 'database with user_id `%s` ' % (updating_user_id,))
250 user = User.get(updating_user_id)
250 user = User.get(updating_user_id)
251 else:
251 else:
252 log.debug('Checking for existing account in RhodeCode '
252 log.debug('Checking for existing account in RhodeCode '
253 'database with username `%s` ' % (username,))
253 'database with username `%s` ' % (username,))
254 user = User.get_by_username(username, case_insensitive=True)
254 user = User.get_by_username(username, case_insensitive=True)
255
255
256 if user is None:
256 if user is None:
257 # we check internal flag if this method is actually allowed to
257 # we check internal flag if this method is actually allowed to
258 # create new user
258 # create new user
259 if not allow_to_create_user:
259 if not allow_to_create_user:
260 msg = ('Method wants to create new user, but it is not '
260 msg = ('Method wants to create new user, but it is not '
261 'allowed to do so')
261 'allowed to do so')
262 log.warning(msg)
262 log.warning(msg)
263 raise NotAllowedToCreateUserError(msg)
263 raise NotAllowedToCreateUserError(msg)
264
264
265 log.debug('Creating new user %s', username)
265 log.debug('Creating new user %s', username)
266
266
267 # only if we create user that is active
267 # only if we create user that is active
268 new_active_user = active
268 new_active_user = active
269 if new_active_user and strict_creation_check:
269 if new_active_user and strict_creation_check:
270 # raises UserCreationError if it's not allowed for any reason to
270 # raises UserCreationError if it's not allowed for any reason to
271 # create new active user, this also executes pre-create hooks
271 # create new active user, this also executes pre-create hooks
272 check_allowed_create_user(user_data, cur_user, strict_check=True)
272 check_allowed_create_user(user_data, cur_user, strict_check=True)
273 events.trigger(events.UserPreCreate(user_data))
273 events.trigger(events.UserPreCreate(user_data))
274 new_user = User()
274 new_user = User()
275 edit = False
275 edit = False
276 else:
276 else:
277 log.debug('updating user %s', username)
277 log.debug('updating user %s', username)
278 events.trigger(events.UserPreUpdate(user, user_data))
278 events.trigger(events.UserPreUpdate(user, user_data))
279 new_user = user
279 new_user = user
280 edit = True
280 edit = True
281
281
282 # we're not allowed to edit default user
282 # we're not allowed to edit default user
283 if user.username == User.DEFAULT_USER:
283 if user.username == User.DEFAULT_USER:
284 raise DefaultUserException(
284 raise DefaultUserException(
285 _("You can't edit this user (`%(username)s`) since it's "
285 _("You can't edit this user (`%(username)s`) since it's "
286 "crucial for entire application") % {'username': user.username})
286 "crucial for entire application") % {'username': user.username})
287
287
288 # inject special attribute that will tell us if User is new or old
288 # inject special attribute that will tell us if User is new or old
289 new_user.is_new_user = not edit
289 new_user.is_new_user = not edit
290 # for users that didn's specify auth type, we use RhodeCode built in
290 # for users that didn's specify auth type, we use RhodeCode built in
291 from rhodecode.authentication.plugins import auth_rhodecode
291 from rhodecode.authentication.plugins import auth_rhodecode
292 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
292 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
293 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
293 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
294
294
295 try:
295 try:
296 new_user.username = username
296 new_user.username = username
297 new_user.admin = admin
297 new_user.admin = admin
298 new_user.email = email
298 new_user.email = email
299 new_user.active = active
299 new_user.active = active
300 new_user.extern_name = safe_unicode(extern_name)
300 new_user.extern_name = safe_unicode(extern_name)
301 new_user.extern_type = safe_unicode(extern_type)
301 new_user.extern_type = safe_unicode(extern_type)
302 new_user.name = firstname
302 new_user.name = firstname
303 new_user.lastname = lastname
303 new_user.lastname = lastname
304
304
305 if not edit:
305 if not edit:
306 new_user.api_key = generate_auth_token(username)
306 new_user.api_key = generate_auth_token(username)
307
307
308 # set password only if creating an user or password is changed
308 # set password only if creating an user or password is changed
309 if not edit or _password_change(new_user, password):
309 if not edit or _password_change(new_user, password):
310 reason = 'new password' if edit else 'new user'
310 reason = 'new password' if edit else 'new user'
311 log.debug('Updating password reason=>%s', reason)
311 log.debug('Updating password reason=>%s', reason)
312 new_user.password = get_crypt_password(password) if password else None
312 new_user.password = get_crypt_password(password) if password else None
313
313
314 if force_password_change:
314 if force_password_change:
315 new_user.update_userdata(force_password_change=True)
315 new_user.update_userdata(force_password_change=True)
316 if language:
316 if language:
317 new_user.update_userdata(language=language)
317 new_user.update_userdata(language=language)
318
318
319 self.sa.add(new_user)
319 self.sa.add(new_user)
320
320
321 if not edit and create_repo_group:
321 if not edit and create_repo_group:
322 # create new group same as username, and make this user an owner
322 # create new group same as username, and make this user an owner
323 desc = RepoGroupModel.PERSONAL_GROUP_DESC % {'username': username}
323 desc = RepoGroupModel.PERSONAL_GROUP_DESC % {'username': username}
324 RepoGroupModel().create(group_name=username,
324 RepoGroupModel().create(group_name=username,
325 group_description=desc,
325 group_description=desc,
326 owner=username, commit_early=False)
326 owner=username, commit_early=False)
327 if not edit:
327 if not edit:
328 # add the RSS token
328 # add the RSS token
329 AuthTokenModel().create(username,
329 AuthTokenModel().create(username,
330 description='Generated feed token',
330 description='Generated feed token',
331 role=AuthTokenModel.cls.ROLE_FEED)
331 role=AuthTokenModel.cls.ROLE_FEED)
332 log_create_user(created_by=cur_user, **new_user.get_dict())
332 log_create_user(created_by=cur_user, **new_user.get_dict())
333 return new_user
333 return new_user
334 except (DatabaseError,):
334 except (DatabaseError,):
335 log.error(traceback.format_exc())
335 log.error(traceback.format_exc())
336 raise
336 raise
337
337
338 def create_registration(self, form_data):
338 def create_registration(self, form_data):
339 from rhodecode.model.notification import NotificationModel
339 from rhodecode.model.notification import NotificationModel
340 from rhodecode.model.notification import EmailNotificationModel
340 from rhodecode.model.notification import EmailNotificationModel
341
341
342 try:
342 try:
343 form_data['admin'] = False
343 form_data['admin'] = False
344 form_data['extern_name'] = 'rhodecode'
344 form_data['extern_name'] = 'rhodecode'
345 form_data['extern_type'] = 'rhodecode'
345 form_data['extern_type'] = 'rhodecode'
346 new_user = self.create(form_data)
346 new_user = self.create(form_data)
347
347
348 self.sa.add(new_user)
348 self.sa.add(new_user)
349 self.sa.flush()
349 self.sa.flush()
350
350
351 user_data = new_user.get_dict()
351 user_data = new_user.get_dict()
352 kwargs = {
352 kwargs = {
353 # use SQLALCHEMY safe dump of user data
353 # use SQLALCHEMY safe dump of user data
354 'user': AttributeDict(user_data),
354 'user': AttributeDict(user_data),
355 'date': datetime.datetime.now()
355 'date': datetime.datetime.now()
356 }
356 }
357 notification_type = EmailNotificationModel.TYPE_REGISTRATION
357 notification_type = EmailNotificationModel.TYPE_REGISTRATION
358 # pre-generate the subject for notification itself
358 # pre-generate the subject for notification itself
359 (subject,
359 (subject,
360 _h, _e, # we don't care about those
360 _h, _e, # we don't care about those
361 body_plaintext) = EmailNotificationModel().render_email(
361 body_plaintext) = EmailNotificationModel().render_email(
362 notification_type, **kwargs)
362 notification_type, **kwargs)
363
363
364 # create notification objects, and emails
364 # create notification objects, and emails
365 NotificationModel().create(
365 NotificationModel().create(
366 created_by=new_user,
366 created_by=new_user,
367 notification_subject=subject,
367 notification_subject=subject,
368 notification_body=body_plaintext,
368 notification_body=body_plaintext,
369 notification_type=notification_type,
369 notification_type=notification_type,
370 recipients=None, # all admins
370 recipients=None, # all admins
371 email_kwargs=kwargs,
371 email_kwargs=kwargs,
372 )
372 )
373
373
374 return new_user
374 return new_user
375 except Exception:
375 except Exception:
376 log.error(traceback.format_exc())
376 log.error(traceback.format_exc())
377 raise
377 raise
378
378
379 def _handle_user_repos(self, username, repositories, handle_mode=None):
379 def _handle_user_repos(self, username, repositories, handle_mode=None):
380 _superadmin = self.cls.get_first_super_admin()
380 _superadmin = self.cls.get_first_super_admin()
381 left_overs = True
381 left_overs = True
382
382
383 from rhodecode.model.repo import RepoModel
383 from rhodecode.model.repo import RepoModel
384
384
385 if handle_mode == 'detach':
385 if handle_mode == 'detach':
386 for obj in repositories:
386 for obj in repositories:
387 obj.user = _superadmin
387 obj.user = _superadmin
388 # set description we know why we super admin now owns
388 # set description we know why we super admin now owns
389 # additional repositories that were orphaned !
389 # additional repositories that were orphaned !
390 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
390 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
391 self.sa.add(obj)
391 self.sa.add(obj)
392 left_overs = False
392 left_overs = False
393 elif handle_mode == 'delete':
393 elif handle_mode == 'delete':
394 for obj in repositories:
394 for obj in repositories:
395 RepoModel().delete(obj, forks='detach')
395 RepoModel().delete(obj, forks='detach')
396 left_overs = False
396 left_overs = False
397
397
398 # if nothing is done we have left overs left
398 # if nothing is done we have left overs left
399 return left_overs
399 return left_overs
400
400
401 def _handle_user_repo_groups(self, username, repository_groups,
401 def _handle_user_repo_groups(self, username, repository_groups,
402 handle_mode=None):
402 handle_mode=None):
403 _superadmin = self.cls.get_first_super_admin()
403 _superadmin = self.cls.get_first_super_admin()
404 left_overs = True
404 left_overs = True
405
405
406 from rhodecode.model.repo_group import RepoGroupModel
406 from rhodecode.model.repo_group import RepoGroupModel
407
407
408 if handle_mode == 'detach':
408 if handle_mode == 'detach':
409 for r in repository_groups:
409 for r in repository_groups:
410 r.user = _superadmin
410 r.user = _superadmin
411 # set description we know why we super admin now owns
411 # set description we know why we super admin now owns
412 # additional repositories that were orphaned !
412 # additional repositories that were orphaned !
413 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
413 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
414 self.sa.add(r)
414 self.sa.add(r)
415 left_overs = False
415 left_overs = False
416 elif handle_mode == 'delete':
416 elif handle_mode == 'delete':
417 for r in repository_groups:
417 for r in repository_groups:
418 RepoGroupModel().delete(r)
418 RepoGroupModel().delete(r)
419 left_overs = False
419 left_overs = False
420
420
421 # if nothing is done we have left overs left
421 # if nothing is done we have left overs left
422 return left_overs
422 return left_overs
423
423
424 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
424 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
425 _superadmin = self.cls.get_first_super_admin()
425 _superadmin = self.cls.get_first_super_admin()
426 left_overs = True
426 left_overs = True
427
427
428 from rhodecode.model.user_group import UserGroupModel
428 from rhodecode.model.user_group import UserGroupModel
429
429
430 if handle_mode == 'detach':
430 if handle_mode == 'detach':
431 for r in user_groups:
431 for r in user_groups:
432 for user_user_group_to_perm in r.user_user_group_to_perm:
432 for user_user_group_to_perm in r.user_user_group_to_perm:
433 if user_user_group_to_perm.user.username == username:
433 if user_user_group_to_perm.user.username == username:
434 user_user_group_to_perm.user = _superadmin
434 user_user_group_to_perm.user = _superadmin
435 r.user = _superadmin
435 r.user = _superadmin
436 # set description we know why we super admin now owns
436 # set description we know why we super admin now owns
437 # additional repositories that were orphaned !
437 # additional repositories that were orphaned !
438 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
438 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
439 self.sa.add(r)
439 self.sa.add(r)
440 left_overs = False
440 left_overs = False
441 elif handle_mode == 'delete':
441 elif handle_mode == 'delete':
442 for r in user_groups:
442 for r in user_groups:
443 UserGroupModel().delete(r)
443 UserGroupModel().delete(r)
444 left_overs = False
444 left_overs = False
445
445
446 # if nothing is done we have left overs left
446 # if nothing is done we have left overs left
447 return left_overs
447 return left_overs
448
448
449 def delete(self, user, cur_user=None, handle_repos=None,
449 def delete(self, user, cur_user=None, handle_repos=None,
450 handle_repo_groups=None, handle_user_groups=None):
450 handle_repo_groups=None, handle_user_groups=None):
451 if not cur_user:
451 if not cur_user:
452 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
452 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
453 user = self._get_user(user)
453 user = self._get_user(user)
454
454
455 try:
455 try:
456 if user.username == User.DEFAULT_USER:
456 if user.username == User.DEFAULT_USER:
457 raise DefaultUserException(
457 raise DefaultUserException(
458 _(u"You can't remove this user since it's"
458 _(u"You can't remove this user since it's"
459 u" crucial for entire application"))
459 u" crucial for entire application"))
460
460
461 left_overs = self._handle_user_repos(
461 left_overs = self._handle_user_repos(
462 user.username, user.repositories, handle_repos)
462 user.username, user.repositories, handle_repos)
463 if left_overs and user.repositories:
463 if left_overs and user.repositories:
464 repos = [x.repo_name for x in user.repositories]
464 repos = [x.repo_name for x in user.repositories]
465 raise UserOwnsReposException(
465 raise UserOwnsReposException(
466 _(u'user "%s" still owns %s repositories and cannot be '
466 _(u'user "%s" still owns %s repositories and cannot be '
467 u'removed. Switch owners or remove those repositories:%s')
467 u'removed. Switch owners or remove those repositories:%s')
468 % (user.username, len(repos), ', '.join(repos)))
468 % (user.username, len(repos), ', '.join(repos)))
469
469
470 left_overs = self._handle_user_repo_groups(
470 left_overs = self._handle_user_repo_groups(
471 user.username, user.repository_groups, handle_repo_groups)
471 user.username, user.repository_groups, handle_repo_groups)
472 if left_overs and user.repository_groups:
472 if left_overs and user.repository_groups:
473 repo_groups = [x.group_name for x in user.repository_groups]
473 repo_groups = [x.group_name for x in user.repository_groups]
474 raise UserOwnsRepoGroupsException(
474 raise UserOwnsRepoGroupsException(
475 _(u'user "%s" still owns %s repository groups and cannot be '
475 _(u'user "%s" still owns %s repository groups and cannot be '
476 u'removed. Switch owners or remove those repository groups:%s')
476 u'removed. Switch owners or remove those repository groups:%s')
477 % (user.username, len(repo_groups), ', '.join(repo_groups)))
477 % (user.username, len(repo_groups), ', '.join(repo_groups)))
478
478
479 left_overs = self._handle_user_user_groups(
479 left_overs = self._handle_user_user_groups(
480 user.username, user.user_groups, handle_user_groups)
480 user.username, user.user_groups, handle_user_groups)
481 if left_overs and user.user_groups:
481 if left_overs and user.user_groups:
482 user_groups = [x.users_group_name for x in user.user_groups]
482 user_groups = [x.users_group_name for x in user.user_groups]
483 raise UserOwnsUserGroupsException(
483 raise UserOwnsUserGroupsException(
484 _(u'user "%s" still owns %s user groups and cannot be '
484 _(u'user "%s" still owns %s user groups and cannot be '
485 u'removed. Switch owners or remove those user groups:%s')
485 u'removed. Switch owners or remove those user groups:%s')
486 % (user.username, len(user_groups), ', '.join(user_groups)))
486 % (user.username, len(user_groups), ', '.join(user_groups)))
487
487
488 # we might change the user data with detach/delete, make sure
488 # we might change the user data with detach/delete, make sure
489 # the object is marked as expired before actually deleting !
489 # the object is marked as expired before actually deleting !
490 self.sa.expire(user)
490 self.sa.expire(user)
491 self.sa.delete(user)
491 self.sa.delete(user)
492 from rhodecode.lib.hooks_base import log_delete_user
492 from rhodecode.lib.hooks_base import log_delete_user
493 log_delete_user(deleted_by=cur_user, **user.get_dict())
493 log_delete_user(deleted_by=cur_user, **user.get_dict())
494 except Exception:
494 except Exception:
495 log.error(traceback.format_exc())
495 log.error(traceback.format_exc())
496 raise
496 raise
497
497
498 def reset_password_link(self, data, pwd_reset_url):
498 def reset_password_link(self, data, pwd_reset_url):
499 from rhodecode.lib.celerylib import tasks, run_task
499 from rhodecode.lib.celerylib import tasks, run_task
500 from rhodecode.model.notification import EmailNotificationModel
500 from rhodecode.model.notification import EmailNotificationModel
501 user_email = data['email']
501 user_email = data['email']
502 try:
502 try:
503 user = User.get_by_email(user_email)
503 user = User.get_by_email(user_email)
504 if user:
504 if user:
505 log.debug('password reset user found %s', user)
505 log.debug('password reset user found %s', user)
506
506
507 email_kwargs = {
507 email_kwargs = {
508 'password_reset_url': pwd_reset_url,
508 'password_reset_url': pwd_reset_url,
509 'user': user,
509 'user': user,
510 'email': user_email,
510 'email': user_email,
511 'date': datetime.datetime.now()
511 'date': datetime.datetime.now()
512 }
512 }
513
513
514 (subject, headers, email_body,
514 (subject, headers, email_body,
515 email_body_plaintext) = EmailNotificationModel().render_email(
515 email_body_plaintext) = EmailNotificationModel().render_email(
516 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
516 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
517
517
518 recipients = [user_email]
518 recipients = [user_email]
519
519
520 action_logger_generic(
520 action_logger_generic(
521 'sending password reset email to user: {}'.format(
521 'sending password reset email to user: {}'.format(
522 user), namespace='security.password_reset')
522 user), namespace='security.password_reset')
523
523
524 run_task(tasks.send_email, recipients, subject,
524 run_task(tasks.send_email, recipients, subject,
525 email_body_plaintext, email_body)
525 email_body_plaintext, email_body)
526
526
527 else:
527 else:
528 log.debug("password reset email %s not found", user_email)
528 log.debug("password reset email %s not found", user_email)
529 except Exception:
529 except Exception:
530 log.error(traceback.format_exc())
530 log.error(traceback.format_exc())
531 return False
531 return False
532
532
533 return True
533 return True
534
534
535 def reset_password(self, data):
535 def reset_password(self, data, pwd_reset_url):
536 from rhodecode.lib.celerylib import tasks, run_task
536 from rhodecode.lib.celerylib import tasks, run_task
537 from rhodecode.model.notification import EmailNotificationModel
537 from rhodecode.model.notification import EmailNotificationModel
538 from rhodecode.lib import auth
538 from rhodecode.lib import auth
539 user_email = data['email']
539 user_email = data['email']
540 pre_db = True
540 pre_db = True
541 try:
541 try:
542 user = User.get_by_email(user_email)
542 user = User.get_by_email(user_email)
543 new_passwd = auth.PasswordGenerator().gen_password(
543 new_passwd = auth.PasswordGenerator().gen_password(
544 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
544 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
545 if user:
545 if user:
546 user.password = auth.get_crypt_password(new_passwd)
546 user.password = auth.get_crypt_password(new_passwd)
547 # also force this user to reset his password !
547 # also force this user to reset his password !
548 user.update_userdata(force_password_change=True)
548 user.update_userdata(force_password_change=True)
549
549
550 Session().add(user)
550 Session().add(user)
551 Session().commit()
551 Session().commit()
552 log.info('change password for %s', user_email)
552 log.info('change password for %s', user_email)
553 if new_passwd is None:
553 if new_passwd is None:
554 raise Exception('unable to generate new password')
554 raise Exception('unable to generate new password')
555
555
556 pre_db = False
556 pre_db = False
557
557
558 email_kwargs = {
558 email_kwargs = {
559 'new_password': new_passwd,
559 'new_password': new_passwd,
560 'password_reset_url': pwd_reset_url,
560 'user': user,
561 'user': user,
561 'email': user_email,
562 'email': user_email,
562 'date': datetime.datetime.now()
563 'date': datetime.datetime.now()
563 }
564 }
564
565
565 (subject, headers, email_body,
566 (subject, headers, email_body,
566 email_body_plaintext) = EmailNotificationModel().render_email(
567 email_body_plaintext) = EmailNotificationModel().render_email(
567 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION, **email_kwargs)
568 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION, **email_kwargs)
568
569
569 recipients = [user_email]
570 recipients = [user_email]
570
571
571 action_logger_generic(
572 action_logger_generic(
572 'sent new password to user: {} with email: {}'.format(
573 'sent new password to user: {} with email: {}'.format(
573 user, user_email), namespace='security.password_reset')
574 user, user_email), namespace='security.password_reset')
574
575
575 run_task(tasks.send_email, recipients, subject,
576 run_task(tasks.send_email, recipients, subject,
576 email_body_plaintext, email_body)
577 email_body_plaintext, email_body)
577
578
578 except Exception:
579 except Exception:
579 log.error('Failed to update user password')
580 log.error('Failed to update user password')
580 log.error(traceback.format_exc())
581 log.error(traceback.format_exc())
581 if pre_db:
582 if pre_db:
582 # we rollback only if local db stuff fails. If it goes into
583 # we rollback only if local db stuff fails. If it goes into
583 # run_task, we're pass rollback state this wouldn't work then
584 # run_task, we're pass rollback state this wouldn't work then
584 Session().rollback()
585 Session().rollback()
585
586
586 return True
587 return True
587
588
588 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
589 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
589 """
590 """
590 Fetches auth_user by user_id,or api_key if present.
591 Fetches auth_user by user_id,or api_key if present.
591 Fills auth_user attributes with those taken from database.
592 Fills auth_user attributes with those taken from database.
592 Additionally set's is_authenitated if lookup fails
593 Additionally set's is_authenitated if lookup fails
593 present in database
594 present in database
594
595
595 :param auth_user: instance of user to set attributes
596 :param auth_user: instance of user to set attributes
596 :param user_id: user id to fetch by
597 :param user_id: user id to fetch by
597 :param api_key: api key to fetch by
598 :param api_key: api key to fetch by
598 :param username: username to fetch by
599 :param username: username to fetch by
599 """
600 """
600 if user_id is None and api_key is None and username is None:
601 if user_id is None and api_key is None and username is None:
601 raise Exception('You need to pass user_id, api_key or username')
602 raise Exception('You need to pass user_id, api_key or username')
602
603
603 log.debug(
604 log.debug(
604 'doing fill data based on: user_id:%s api_key:%s username:%s',
605 'doing fill data based on: user_id:%s api_key:%s username:%s',
605 user_id, api_key, username)
606 user_id, api_key, username)
606 try:
607 try:
607 dbuser = None
608 dbuser = None
608 if user_id:
609 if user_id:
609 dbuser = self.get(user_id)
610 dbuser = self.get(user_id)
610 elif api_key:
611 elif api_key:
611 dbuser = self.get_by_auth_token(api_key)
612 dbuser = self.get_by_auth_token(api_key)
612 elif username:
613 elif username:
613 dbuser = self.get_by_username(username)
614 dbuser = self.get_by_username(username)
614
615
615 if not dbuser:
616 if not dbuser:
616 log.warning(
617 log.warning(
617 'Unable to lookup user by id:%s api_key:%s username:%s',
618 'Unable to lookup user by id:%s api_key:%s username:%s',
618 user_id, api_key, username)
619 user_id, api_key, username)
619 return False
620 return False
620 if not dbuser.active:
621 if not dbuser.active:
621 log.debug('User `%s` is inactive, skipping fill data', username)
622 log.debug('User `%s` is inactive, skipping fill data', username)
622 return False
623 return False
623
624
624 log.debug('filling user:%s data', dbuser)
625 log.debug('filling user:%s data', dbuser)
625
626
626 # TODO: johbo: Think about this and find a clean solution
627 # TODO: johbo: Think about this and find a clean solution
627 user_data = dbuser.get_dict()
628 user_data = dbuser.get_dict()
628 user_data.update(dbuser.get_api_data(include_secrets=True))
629 user_data.update(dbuser.get_api_data(include_secrets=True))
629
630
630 for k, v in user_data.iteritems():
631 for k, v in user_data.iteritems():
631 # properties of auth user we dont update
632 # properties of auth user we dont update
632 if k not in ['auth_tokens', 'permissions']:
633 if k not in ['auth_tokens', 'permissions']:
633 setattr(auth_user, k, v)
634 setattr(auth_user, k, v)
634
635
635 # few extras
636 # few extras
636 setattr(auth_user, 'feed_token', dbuser.feed_token)
637 setattr(auth_user, 'feed_token', dbuser.feed_token)
637 except Exception:
638 except Exception:
638 log.error(traceback.format_exc())
639 log.error(traceback.format_exc())
639 auth_user.is_authenticated = False
640 auth_user.is_authenticated = False
640 return False
641 return False
641
642
642 return True
643 return True
643
644
644 def has_perm(self, user, perm):
645 def has_perm(self, user, perm):
645 perm = self._get_perm(perm)
646 perm = self._get_perm(perm)
646 user = self._get_user(user)
647 user = self._get_user(user)
647
648
648 return UserToPerm.query().filter(UserToPerm.user == user)\
649 return UserToPerm.query().filter(UserToPerm.user == user)\
649 .filter(UserToPerm.permission == perm).scalar() is not None
650 .filter(UserToPerm.permission == perm).scalar() is not None
650
651
651 def grant_perm(self, user, perm):
652 def grant_perm(self, user, perm):
652 """
653 """
653 Grant user global permissions
654 Grant user global permissions
654
655
655 :param user:
656 :param user:
656 :param perm:
657 :param perm:
657 """
658 """
658 user = self._get_user(user)
659 user = self._get_user(user)
659 perm = self._get_perm(perm)
660 perm = self._get_perm(perm)
660 # if this permission is already granted skip it
661 # if this permission is already granted skip it
661 _perm = UserToPerm.query()\
662 _perm = UserToPerm.query()\
662 .filter(UserToPerm.user == user)\
663 .filter(UserToPerm.user == user)\
663 .filter(UserToPerm.permission == perm)\
664 .filter(UserToPerm.permission == perm)\
664 .scalar()
665 .scalar()
665 if _perm:
666 if _perm:
666 return
667 return
667 new = UserToPerm()
668 new = UserToPerm()
668 new.user = user
669 new.user = user
669 new.permission = perm
670 new.permission = perm
670 self.sa.add(new)
671 self.sa.add(new)
671 return new
672 return new
672
673
673 def revoke_perm(self, user, perm):
674 def revoke_perm(self, user, perm):
674 """
675 """
675 Revoke users global permissions
676 Revoke users global permissions
676
677
677 :param user:
678 :param user:
678 :param perm:
679 :param perm:
679 """
680 """
680 user = self._get_user(user)
681 user = self._get_user(user)
681 perm = self._get_perm(perm)
682 perm = self._get_perm(perm)
682
683
683 obj = UserToPerm.query()\
684 obj = UserToPerm.query()\
684 .filter(UserToPerm.user == user)\
685 .filter(UserToPerm.user == user)\
685 .filter(UserToPerm.permission == perm)\
686 .filter(UserToPerm.permission == perm)\
686 .scalar()
687 .scalar()
687 if obj:
688 if obj:
688 self.sa.delete(obj)
689 self.sa.delete(obj)
689
690
690 def add_extra_email(self, user, email):
691 def add_extra_email(self, user, email):
691 """
692 """
692 Adds email address to UserEmailMap
693 Adds email address to UserEmailMap
693
694
694 :param user:
695 :param user:
695 :param email:
696 :param email:
696 """
697 """
697 from rhodecode.model import forms
698 from rhodecode.model import forms
698 form = forms.UserExtraEmailForm()()
699 form = forms.UserExtraEmailForm()()
699 data = form.to_python({'email': email})
700 data = form.to_python({'email': email})
700 user = self._get_user(user)
701 user = self._get_user(user)
701
702
702 obj = UserEmailMap()
703 obj = UserEmailMap()
703 obj.user = user
704 obj.user = user
704 obj.email = data['email']
705 obj.email = data['email']
705 self.sa.add(obj)
706 self.sa.add(obj)
706 return obj
707 return obj
707
708
708 def delete_extra_email(self, user, email_id):
709 def delete_extra_email(self, user, email_id):
709 """
710 """
710 Removes email address from UserEmailMap
711 Removes email address from UserEmailMap
711
712
712 :param user:
713 :param user:
713 :param email_id:
714 :param email_id:
714 """
715 """
715 user = self._get_user(user)
716 user = self._get_user(user)
716 obj = UserEmailMap.query().get(email_id)
717 obj = UserEmailMap.query().get(email_id)
717 if obj:
718 if obj:
718 self.sa.delete(obj)
719 self.sa.delete(obj)
719
720
720 def parse_ip_range(self, ip_range):
721 def parse_ip_range(self, ip_range):
721 ip_list = []
722 ip_list = []
722 def make_unique(value):
723 def make_unique(value):
723 seen = []
724 seen = []
724 return [c for c in value if not (c in seen or seen.append(c))]
725 return [c for c in value if not (c in seen or seen.append(c))]
725
726
726 # firsts split by commas
727 # firsts split by commas
727 for ip_range in ip_range.split(','):
728 for ip_range in ip_range.split(','):
728 if not ip_range:
729 if not ip_range:
729 continue
730 continue
730 ip_range = ip_range.strip()
731 ip_range = ip_range.strip()
731 if '-' in ip_range:
732 if '-' in ip_range:
732 start_ip, end_ip = ip_range.split('-', 1)
733 start_ip, end_ip = ip_range.split('-', 1)
733 start_ip = ipaddress.ip_address(start_ip.strip())
734 start_ip = ipaddress.ip_address(start_ip.strip())
734 end_ip = ipaddress.ip_address(end_ip.strip())
735 end_ip = ipaddress.ip_address(end_ip.strip())
735 parsed_ip_range = []
736 parsed_ip_range = []
736
737
737 for index in xrange(int(start_ip), int(end_ip) + 1):
738 for index in xrange(int(start_ip), int(end_ip) + 1):
738 new_ip = ipaddress.ip_address(index)
739 new_ip = ipaddress.ip_address(index)
739 parsed_ip_range.append(str(new_ip))
740 parsed_ip_range.append(str(new_ip))
740 ip_list.extend(parsed_ip_range)
741 ip_list.extend(parsed_ip_range)
741 else:
742 else:
742 ip_list.append(ip_range)
743 ip_list.append(ip_range)
743
744
744 return make_unique(ip_list)
745 return make_unique(ip_list)
745
746
746 def add_extra_ip(self, user, ip, description=None):
747 def add_extra_ip(self, user, ip, description=None):
747 """
748 """
748 Adds ip address to UserIpMap
749 Adds ip address to UserIpMap
749
750
750 :param user:
751 :param user:
751 :param ip:
752 :param ip:
752 """
753 """
753 from rhodecode.model import forms
754 from rhodecode.model import forms
754 form = forms.UserExtraIpForm()()
755 form = forms.UserExtraIpForm()()
755 data = form.to_python({'ip': ip})
756 data = form.to_python({'ip': ip})
756 user = self._get_user(user)
757 user = self._get_user(user)
757
758
758 obj = UserIpMap()
759 obj = UserIpMap()
759 obj.user = user
760 obj.user = user
760 obj.ip_addr = data['ip']
761 obj.ip_addr = data['ip']
761 obj.description = description
762 obj.description = description
762 self.sa.add(obj)
763 self.sa.add(obj)
763 return obj
764 return obj
764
765
765 def delete_extra_ip(self, user, ip_id):
766 def delete_extra_ip(self, user, ip_id):
766 """
767 """
767 Removes ip address from UserIpMap
768 Removes ip address from UserIpMap
768
769
769 :param user:
770 :param user:
770 :param ip_id:
771 :param ip_id:
771 """
772 """
772 user = self._get_user(user)
773 user = self._get_user(user)
773 obj = UserIpMap.query().get(ip_id)
774 obj = UserIpMap.query().get(ip_id)
774 if obj:
775 if obj:
775 self.sa.delete(obj)
776 self.sa.delete(obj)
776
777
777 def get_accounts_in_creation_order(self, current_user=None):
778 def get_accounts_in_creation_order(self, current_user=None):
778 """
779 """
779 Get accounts in order of creation for deactivation for license limits
780 Get accounts in order of creation for deactivation for license limits
780
781
781 pick currently logged in user, and append to the list in position 0
782 pick currently logged in user, and append to the list in position 0
782 pick all super-admins in order of creation date and add it to the list
783 pick all super-admins in order of creation date and add it to the list
783 pick all other accounts in order of creation and add it to the list.
784 pick all other accounts in order of creation and add it to the list.
784
785
785 Based on that list, the last accounts can be disabled as they are
786 Based on that list, the last accounts can be disabled as they are
786 created at the end and don't include any of the super admins as well
787 created at the end and don't include any of the super admins as well
787 as the current user.
788 as the current user.
788
789
789 :param current_user: optionally current user running this operation
790 :param current_user: optionally current user running this operation
790 """
791 """
791
792
792 if not current_user:
793 if not current_user:
793 current_user = get_current_rhodecode_user()
794 current_user = get_current_rhodecode_user()
794 active_super_admins = [
795 active_super_admins = [
795 x.user_id for x in User.query()
796 x.user_id for x in User.query()
796 .filter(User.user_id != current_user.user_id)
797 .filter(User.user_id != current_user.user_id)
797 .filter(User.active == true())
798 .filter(User.active == true())
798 .filter(User.admin == true())
799 .filter(User.admin == true())
799 .order_by(User.created_on.asc())]
800 .order_by(User.created_on.asc())]
800
801
801 active_regular_users = [
802 active_regular_users = [
802 x.user_id for x in User.query()
803 x.user_id for x in User.query()
803 .filter(User.user_id != current_user.user_id)
804 .filter(User.user_id != current_user.user_id)
804 .filter(User.active == true())
805 .filter(User.active == true())
805 .filter(User.admin == false())
806 .filter(User.admin == false())
806 .order_by(User.created_on.asc())]
807 .order_by(User.created_on.asc())]
807
808
808 list_of_accounts = [current_user.user_id]
809 list_of_accounts = [current_user.user_id]
809 list_of_accounts += active_super_admins
810 list_of_accounts += active_super_admins
810 list_of_accounts += active_regular_users
811 list_of_accounts += active_regular_users
811
812
812 return list_of_accounts
813 return list_of_accounts
813
814
814 def deactivate_last_users(self, expected_users):
815 def deactivate_last_users(self, expected_users):
815 """
816 """
816 Deactivate accounts that are over the license limits.
817 Deactivate accounts that are over the license limits.
817 Algorithm of which accounts to disabled is based on the formula:
818 Algorithm of which accounts to disabled is based on the formula:
818
819
819 Get current user, then super admins in creation order, then regular
820 Get current user, then super admins in creation order, then regular
820 active users in creation order.
821 active users in creation order.
821
822
822 Using that list we mark all accounts from the end of it as inactive.
823 Using that list we mark all accounts from the end of it as inactive.
823 This way we block only latest created accounts.
824 This way we block only latest created accounts.
824
825
825 :param expected_users: list of users in special order, we deactivate
826 :param expected_users: list of users in special order, we deactivate
826 the end N ammoun of users from that list
827 the end N ammoun of users from that list
827 """
828 """
828
829
829 list_of_accounts = self.get_accounts_in_creation_order()
830 list_of_accounts = self.get_accounts_in_creation_order()
830
831
831 for acc_id in list_of_accounts[expected_users + 1:]:
832 for acc_id in list_of_accounts[expected_users + 1:]:
832 user = User.get(acc_id)
833 user = User.get(acc_id)
833 log.info('Deactivating account %s for license unlock', user)
834 log.info('Deactivating account %s for license unlock', user)
834 user.active = False
835 user.active = False
835 Session().add(user)
836 Session().add(user)
836 Session().commit()
837 Session().commit()
837
838
838 return
839 return
General Comments 0
You need to be logged in to leave comments. Login now