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