##// END OF EJS Templates
login: Fix error when getting remote IP address during registration....
johbo -
r45:c7e16994 default
parent child Browse files
Show More
@@ -1,354 +1,354 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 import uuid
25 import uuid
26
26
27 from pylons import url
27 from pylons import url
28 from pyramid.httpexceptions import HTTPFound
28 from pyramid.httpexceptions import HTTPFound
29 from pyramid.i18n import TranslationStringFactory
29 from pyramid.i18n import TranslationStringFactory
30 from pyramid.view import view_config
30 from pyramid.view import view_config
31 from recaptcha.client.captcha import submit
31 from recaptcha.client.captcha import submit
32
32
33 from rhodecode.authentication.base import loadplugin
33 from rhodecode.authentication.base import loadplugin
34 from rhodecode.events import UserRegistered
34 from rhodecode.events import UserRegistered
35 from rhodecode.lib.auth import (
35 from rhodecode.lib.auth import (
36 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
36 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
37 from rhodecode.lib.base import get_ip_addr
37 from rhodecode.lib.base import get_ip_addr
38 from rhodecode.lib.exceptions import UserCreationError
38 from rhodecode.lib.exceptions import UserCreationError
39 from rhodecode.lib.utils2 import safe_str
39 from rhodecode.lib.utils2 import safe_str
40 from rhodecode.model.db import User
40 from rhodecode.model.db import User
41 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
42 from rhodecode.model.login_session import LoginSession
42 from rhodecode.model.login_session import LoginSession
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.user import UserModel
45 from rhodecode.model.user import UserModel
46
46
47
47
48 _ = TranslationStringFactory('rhodecode-enterprise')
48 _ = TranslationStringFactory('rhodecode-enterprise')
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 def _store_user_in_session(session, username, remember=False):
53 def _store_user_in_session(session, username, remember=False):
54 user = User.get_by_username(username, case_insensitive=True)
54 user = User.get_by_username(username, case_insensitive=True)
55 auth_user = AuthUser(user.user_id)
55 auth_user = AuthUser(user.user_id)
56 auth_user.set_authenticated()
56 auth_user.set_authenticated()
57 cs = auth_user.get_cookie_store()
57 cs = auth_user.get_cookie_store()
58 session['rhodecode_user'] = cs
58 session['rhodecode_user'] = cs
59 user.update_lastlogin()
59 user.update_lastlogin()
60 Session().commit()
60 Session().commit()
61
61
62 # If they want to be remembered, update the cookie
62 # If they want to be remembered, update the cookie
63 if remember:
63 if remember:
64 _year = (datetime.datetime.now() +
64 _year = (datetime.datetime.now() +
65 datetime.timedelta(seconds=60 * 60 * 24 * 365))
65 datetime.timedelta(seconds=60 * 60 * 24 * 365))
66 session._set_cookie_expires(_year)
66 session._set_cookie_expires(_year)
67
67
68 session.save()
68 session.save()
69
69
70 log.info('user %s is now authenticated and stored in '
70 log.info('user %s is now authenticated and stored in '
71 'session, session attrs %s', username, cs)
71 'session, session attrs %s', username, cs)
72
72
73 # dumps session attrs back to cookie
73 # dumps session attrs back to cookie
74 session._update_cookie_out()
74 session._update_cookie_out()
75 # we set new cookie
75 # we set new cookie
76 headers = None
76 headers = None
77 if session.request['set_cookie']:
77 if session.request['set_cookie']:
78 # send set-cookie headers back to response to update cookie
78 # send set-cookie headers back to response to update cookie
79 headers = [('Set-Cookie', session.request['cookie_out'])]
79 headers = [('Set-Cookie', session.request['cookie_out'])]
80 return headers
80 return headers
81
81
82
82
83 class LoginView(object):
83 class LoginView(object):
84
84
85 def __init__(self, context, request):
85 def __init__(self, context, request):
86 self.request = request
86 self.request = request
87 self.context = context
87 self.context = context
88 self.session = request.session
88 self.session = request.session
89 self._rhodecode_user = request.user
89 self._rhodecode_user = request.user
90
90
91 def _validate_came_from(self, came_from):
91 def _validate_came_from(self, came_from):
92 if not came_from:
92 if not came_from:
93 return came_from
93 return came_from
94
94
95 parsed = urlparse.urlparse(came_from)
95 parsed = urlparse.urlparse(came_from)
96 allowed_schemes = ['http', 'https']
96 allowed_schemes = ['http', 'https']
97 if parsed.scheme and parsed.scheme not in allowed_schemes:
97 if parsed.scheme and parsed.scheme not in allowed_schemes:
98 log.error('Suspicious URL scheme detected %s for url %s' %
98 log.error('Suspicious URL scheme detected %s for url %s' %
99 (parsed.scheme, parsed))
99 (parsed.scheme, parsed))
100 came_from = url('home')
100 came_from = url('home')
101 elif parsed.netloc and self.request.host != parsed.netloc:
101 elif parsed.netloc and self.request.host != parsed.netloc:
102 log.error('Suspicious NETLOC detected %s for url %s server url '
102 log.error('Suspicious NETLOC detected %s for url %s server url '
103 'is: %s' % (parsed.netloc, parsed, self.request.host))
103 'is: %s' % (parsed.netloc, parsed, self.request.host))
104 came_from = url('home')
104 came_from = url('home')
105 if any(bad_str in parsed.path for bad_str in ('\r', '\n')):
105 if any(bad_str in parsed.path for bad_str in ('\r', '\n')):
106 log.error('Header injection detected `%s` for url %s server url ' %
106 log.error('Header injection detected `%s` for url %s server url ' %
107 (parsed.path, parsed))
107 (parsed.path, parsed))
108 came_from = url('home')
108 came_from = url('home')
109 return came_from
109 return came_from
110
110
111 def _get_came_from(self):
111 def _get_came_from(self):
112 _default_came_from = url('home')
112 _default_came_from = url('home')
113 came_from = self._validate_came_from(
113 came_from = self._validate_came_from(
114 safe_str(self.request.GET.get('came_from', '')))
114 safe_str(self.request.GET.get('came_from', '')))
115 return came_from or _default_came_from
115 return came_from or _default_came_from
116
116
117 def _get_template_context(self):
117 def _get_template_context(self):
118 return {
118 return {
119 'came_from': self._get_came_from(),
119 'came_from': self._get_came_from(),
120 'defaults': {},
120 'defaults': {},
121 'errors': {},
121 'errors': {},
122 }
122 }
123
123
124 @view_config(
124 @view_config(
125 route_name='login', request_method='GET',
125 route_name='login', request_method='GET',
126 renderer='rhodecode:templates/login.html')
126 renderer='rhodecode:templates/login.html')
127 def login(self):
127 def login(self):
128 user = self.request.user
128 user = self.request.user
129
129
130 # redirect if already logged in
130 # redirect if already logged in
131 if user.is_authenticated and not user.is_default and user.ip_allowed:
131 if user.is_authenticated and not user.is_default and user.ip_allowed:
132 raise HTTPFound(self._get_came_from())
132 raise HTTPFound(self._get_came_from())
133
133
134 return self._get_template_context()
134 return self._get_template_context()
135
135
136 @view_config(
136 @view_config(
137 route_name='login', request_method='POST',
137 route_name='login', request_method='POST',
138 renderer='rhodecode:templates/login.html')
138 renderer='rhodecode:templates/login.html')
139 def login_post(self):
139 def login_post(self):
140 came_from = self._get_came_from()
140 came_from = self._get_came_from()
141 session = self.request.session
141 session = self.request.session
142 login_form = LoginForm()()
142 login_form = LoginForm()()
143
143
144 try:
144 try:
145 session.invalidate()
145 session.invalidate()
146 form_result = login_form.to_python(self.request.params)
146 form_result = login_form.to_python(self.request.params)
147 # form checks for username/password, now we're authenticated
147 # form checks for username/password, now we're authenticated
148 headers = _store_user_in_session(
148 headers = _store_user_in_session(
149 self.session,
149 self.session,
150 username=form_result['username'],
150 username=form_result['username'],
151 remember=form_result['remember'])
151 remember=form_result['remember'])
152 raise HTTPFound(came_from, headers=headers)
152 raise HTTPFound(came_from, headers=headers)
153 except formencode.Invalid as errors:
153 except formencode.Invalid as errors:
154 defaults = errors.value
154 defaults = errors.value
155 # remove password from filling in form again
155 # remove password from filling in form again
156 del defaults['password']
156 del defaults['password']
157 render_ctx = self._get_template_context()
157 render_ctx = self._get_template_context()
158 render_ctx.update({
158 render_ctx.update({
159 'errors': errors.error_dict,
159 'errors': errors.error_dict,
160 'defaults': defaults,
160 'defaults': defaults,
161 })
161 })
162 return render_ctx
162 return render_ctx
163
163
164 except UserCreationError as e:
164 except UserCreationError as e:
165 # container auth or other auth functions that create users on
165 # container auth or other auth functions that create users on
166 # the fly can throw this exception signaling that there's issue
166 # the fly can throw this exception signaling that there's issue
167 # with user creation, explanation should be provided in
167 # with user creation, explanation should be provided in
168 # Exception itself
168 # Exception itself
169 session.flash(e, queue='error')
169 session.flash(e, queue='error')
170
170
171 # check if we use container plugin, and try to login using it.
171 # check if we use container plugin, and try to login using it.
172 from rhodecode.authentication.base import authenticate, HTTP_TYPE
172 from rhodecode.authentication.base import authenticate, HTTP_TYPE
173 try:
173 try:
174 log.debug('Running PRE-AUTH for container based authentication')
174 log.debug('Running PRE-AUTH for container based authentication')
175 auth_info = authenticate(
175 auth_info = authenticate(
176 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
176 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
177 except UserCreationError as e:
177 except UserCreationError as e:
178 log.error(e)
178 log.error(e)
179 session.flash(e, queue='error')
179 session.flash(e, queue='error')
180 # render login, with flash message about limit
180 # render login, with flash message about limit
181 return self._get_template_context()
181 return self._get_template_context()
182
182
183 if auth_info:
183 if auth_info:
184 headers = _store_user_in_session(auth_info.get('username'))
184 headers = _store_user_in_session(auth_info.get('username'))
185 raise HTTPFound(came_from, headers=headers)
185 raise HTTPFound(came_from, headers=headers)
186
186
187 return self._get_template_context()
187 return self._get_template_context()
188
188
189 @CSRFRequired()
189 @CSRFRequired()
190 @view_config(route_name='logout', request_method='POST')
190 @view_config(route_name='logout', request_method='POST')
191 def logout(self):
191 def logout(self):
192 LoginSession().destroy_user_session()
192 LoginSession().destroy_user_session()
193 return HTTPFound(url('home'))
193 return HTTPFound(url('home'))
194
194
195 @HasPermissionAnyDecorator(
195 @HasPermissionAnyDecorator(
196 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
196 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
197 @view_config(
197 @view_config(
198 route_name='register', request_method='GET',
198 route_name='register', request_method='GET',
199 renderer='rhodecode:templates/register.html',)
199 renderer='rhodecode:templates/register.html',)
200 def register(self):
200 def register(self):
201 settings = SettingsModel().get_all_settings()
201 settings = SettingsModel().get_all_settings()
202 captcha_public_key = settings.get('rhodecode_captcha_public_key')
202 captcha_public_key = settings.get('rhodecode_captcha_public_key')
203 captcha_private_key = settings.get('rhodecode_captcha_private_key')
203 captcha_private_key = settings.get('rhodecode_captcha_private_key')
204 captcha_active = bool(captcha_private_key)
204 captcha_active = bool(captcha_private_key)
205 register_message = settings.get('rhodecode_register_message') or ''
205 register_message = settings.get('rhodecode_register_message') or ''
206 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
206 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
207 .AuthUser.permissions['global']
207 .AuthUser.permissions['global']
208
208
209 render_ctx = self._get_template_context()
209 render_ctx = self._get_template_context()
210 render_ctx.update({
210 render_ctx.update({
211 'auto_active': auto_active,
211 'auto_active': auto_active,
212 'captcha_active': captcha_active,
212 'captcha_active': captcha_active,
213 'captcha_public_key': captcha_public_key,
213 'captcha_public_key': captcha_public_key,
214 'register_message': register_message,
214 'register_message': register_message,
215 })
215 })
216 return render_ctx
216 return render_ctx
217
217
218 @view_config(
218 @view_config(
219 route_name='register', request_method='POST',
219 route_name='register', request_method='POST',
220 renderer='rhodecode:templates/register.html')
220 renderer='rhodecode:templates/register.html')
221 def register_post(self):
221 def register_post(self):
222 settings = SettingsModel().get_all_settings()
222 settings = SettingsModel().get_all_settings()
223 captcha_private_key = settings.get('rhodecode_captcha_private_key')
223 captcha_private_key = settings.get('rhodecode_captcha_private_key')
224 captcha_active = bool(captcha_private_key)
224 captcha_active = bool(captcha_private_key)
225 register_message = settings.get('rhodecode_register_message') or ''
225 register_message = settings.get('rhodecode_register_message') or ''
226 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
226 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
227 .AuthUser.permissions['global']
227 .AuthUser.permissions['global']
228
228
229 register_form = RegisterForm()()
229 register_form = RegisterForm()()
230 try:
230 try:
231 form_result = register_form.to_python(self.request.params)
231 form_result = register_form.to_python(self.request.params)
232 form_result['active'] = auto_active
232 form_result['active'] = auto_active
233
233
234 if captcha_active:
234 if captcha_active:
235 response = submit(
235 response = submit(
236 self.request.params.get('recaptcha_challenge_field'),
236 self.request.params.get('recaptcha_challenge_field'),
237 self.request.params.get('recaptcha_response_field'),
237 self.request.params.get('recaptcha_response_field'),
238 private_key=captcha_private_key,
238 private_key=captcha_private_key,
239 remoteip=self.ip_addr)
239 remoteip=get_ip_addr(self.request.environ))
240 if captcha_active and not response.is_valid:
240 if captcha_active and not response.is_valid:
241 _value = form_result
241 _value = form_result
242 _msg = _('bad captcha')
242 _msg = _('bad captcha')
243 error_dict = {'recaptcha_field': _msg}
243 error_dict = {'recaptcha_field': _msg}
244 raise formencode.Invalid(_msg, _value, None,
244 raise formencode.Invalid(_msg, _value, None,
245 error_dict=error_dict)
245 error_dict=error_dict)
246
246
247 new_user = UserModel().create_registration(form_result)
247 new_user = UserModel().create_registration(form_result)
248 event = UserRegistered(user=new_user, session=self.session)
248 event = UserRegistered(user=new_user, session=self.session)
249 self.request.registry.notify(event)
249 self.request.registry.notify(event)
250 self.session.flash(
250 self.session.flash(
251 _('You have successfully registered with RhodeCode'),
251 _('You have successfully registered with RhodeCode'),
252 queue='success')
252 queue='success')
253 Session().commit()
253 Session().commit()
254
254
255 redirect_ro = self.request.route_path('login')
255 redirect_ro = self.request.route_path('login')
256 raise HTTPFound(redirect_ro)
256 raise HTTPFound(redirect_ro)
257
257
258 except formencode.Invalid as errors:
258 except formencode.Invalid as errors:
259 del errors.value['password']
259 del errors.value['password']
260 del errors.value['password_confirmation']
260 del errors.value['password_confirmation']
261 render_ctx = self._get_template_context()
261 render_ctx = self._get_template_context()
262 render_ctx.update({
262 render_ctx.update({
263 'errors': errors.error_dict,
263 'errors': errors.error_dict,
264 'defaults': errors.value,
264 'defaults': errors.value,
265 'register_message': register_message,
265 'register_message': register_message,
266 })
266 })
267 return render_ctx
267 return render_ctx
268
268
269 except UserCreationError as e:
269 except UserCreationError as e:
270 # container auth or other auth functions that create users on
270 # container auth or other auth functions that create users on
271 # the fly can throw this exception signaling that there's issue
271 # the fly can throw this exception signaling that there's issue
272 # with user creation, explanation should be provided in
272 # with user creation, explanation should be provided in
273 # Exception itself
273 # Exception itself
274 self.session.flash(e, queue='error')
274 self.session.flash(e, queue='error')
275 render_ctx = self._get_template_context()
275 render_ctx = self._get_template_context()
276 render_ctx.update({
276 render_ctx.update({
277 'register_message': register_message,
277 'register_message': register_message,
278 })
278 })
279 return render_ctx
279 return render_ctx
280
280
281 @view_config(
281 @view_config(
282 route_name='reset_password', request_method=('GET', 'POST'),
282 route_name='reset_password', request_method=('GET', 'POST'),
283 renderer='rhodecode:templates/password_reset.html')
283 renderer='rhodecode:templates/password_reset.html')
284 def password_reset(self):
284 def password_reset(self):
285 settings = SettingsModel().get_all_settings()
285 settings = SettingsModel().get_all_settings()
286 captcha_private_key = settings.get('rhodecode_captcha_private_key')
286 captcha_private_key = settings.get('rhodecode_captcha_private_key')
287 captcha_active = bool(captcha_private_key)
287 captcha_active = bool(captcha_private_key)
288 captcha_public_key = settings.get('rhodecode_captcha_public_key')
288 captcha_public_key = settings.get('rhodecode_captcha_public_key')
289
289
290 render_ctx = {
290 render_ctx = {
291 'captcha_active': captcha_active,
291 'captcha_active': captcha_active,
292 'captcha_public_key': captcha_public_key,
292 'captcha_public_key': captcha_public_key,
293 'defaults': {},
293 'defaults': {},
294 'errors': {},
294 'errors': {},
295 }
295 }
296
296
297 if self.request.POST:
297 if self.request.POST:
298 password_reset_form = PasswordResetForm()()
298 password_reset_form = PasswordResetForm()()
299 try:
299 try:
300 form_result = password_reset_form.to_python(
300 form_result = password_reset_form.to_python(
301 self.request.params)
301 self.request.params)
302 if captcha_active:
302 if captcha_active:
303 response = submit(
303 response = submit(
304 self.request.params.get('recaptcha_challenge_field'),
304 self.request.params.get('recaptcha_challenge_field'),
305 self.request.params.get('recaptcha_response_field'),
305 self.request.params.get('recaptcha_response_field'),
306 private_key=captcha_private_key,
306 private_key=captcha_private_key,
307 remoteip=get_ip_addr(self.request.environ))
307 remoteip=get_ip_addr(self.request.environ))
308 if captcha_active and not response.is_valid:
308 if captcha_active and not response.is_valid:
309 _value = form_result
309 _value = form_result
310 _msg = _('bad captcha')
310 _msg = _('bad captcha')
311 error_dict = {'recaptcha_field': _msg}
311 error_dict = {'recaptcha_field': _msg}
312 raise formencode.Invalid(_msg, _value, None,
312 raise formencode.Invalid(_msg, _value, None,
313 error_dict=error_dict)
313 error_dict=error_dict)
314
314
315 # Generate reset URL and send mail.
315 # Generate reset URL and send mail.
316 user_email = form_result['email']
316 user_email = form_result['email']
317 user = User.get_by_email(user_email)
317 user = User.get_by_email(user_email)
318 password_reset_url = self.request.route_url(
318 password_reset_url = self.request.route_url(
319 'reset_password_confirmation',
319 'reset_password_confirmation',
320 _query={'key': user.api_key})
320 _query={'key': user.api_key})
321 UserModel().reset_password_link(
321 UserModel().reset_password_link(
322 form_result, password_reset_url)
322 form_result, password_reset_url)
323
323
324 # Display success message and redirect.
324 # Display success message and redirect.
325 self.session.flash(
325 self.session.flash(
326 _('Your password reset link was sent'),
326 _('Your password reset link was sent'),
327 queue='success')
327 queue='success')
328 return HTTPFound(self.request.route_path('login'))
328 return HTTPFound(self.request.route_path('login'))
329
329
330 except formencode.Invalid as errors:
330 except formencode.Invalid as errors:
331 render_ctx.update({
331 render_ctx.update({
332 'defaults': errors.value,
332 'defaults': errors.value,
333 'errors': errors.error_dict,
333 'errors': errors.error_dict,
334 })
334 })
335
335
336 return render_ctx
336 return render_ctx
337
337
338 @view_config(route_name='reset_password_confirmation',
338 @view_config(route_name='reset_password_confirmation',
339 request_method='GET')
339 request_method='GET')
340 def password_reset_confirmation(self):
340 def password_reset_confirmation(self):
341 if self.request.GET and self.request.GET.get('key'):
341 if self.request.GET and self.request.GET.get('key'):
342 try:
342 try:
343 user = User.get_by_auth_token(self.request.GET.get('key'))
343 user = User.get_by_auth_token(self.request.GET.get('key'))
344 data = {'email': user.email}
344 data = {'email': user.email}
345 UserModel().reset_password(data)
345 UserModel().reset_password(data)
346 self.session.flash(
346 self.session.flash(
347 _('Your password reset was successful, '
347 _('Your password reset was successful, '
348 'a new password has been sent to your email'),
348 'a new password has been sent to your email'),
349 queue='success')
349 queue='success')
350 except Exception as e:
350 except Exception as e:
351 log.error(e)
351 log.error(e)
352 return HTTPFound(self.request.route_path('reset_password'))
352 return HTTPFound(self.request.route_path('reset_password'))
353
353
354 return HTTPFound(self.request.route_path('login'))
354 return HTTPFound(self.request.route_path('login'))
General Comments 0
You need to be logged in to leave comments. Login now