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