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