##// 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 # -*- 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 raise HTTPFound(came_from, headers=headers)
158 raise HTTPFound(came_from, headers=headers)
158 except formencode.Invalid as errors:
159 except formencode.Invalid as errors:
159 defaults = errors.value
160 defaults = errors.value
160 # remove password from filling in form again
161 # remove password from filling in form again
161 del defaults['password']
162 del defaults['password']
162 render_ctx = self._get_template_context()
163 render_ctx = self._get_template_context()
163 render_ctx.update({
164 render_ctx.update({
164 'errors': errors.error_dict,
165 'errors': errors.error_dict,
165 'defaults': defaults,
166 'defaults': defaults,
166 })
167 })
167 return render_ctx
168 return render_ctx
168
169
169 except UserCreationError as e:
170 except UserCreationError as e:
170 # headers auth or other auth functions that create users on
171 # headers auth or other auth functions that create users on
171 # the fly can throw this exception signaling that there's issue
172 # the fly can throw this exception signaling that there's issue
172 # with user creation, explanation should be provided in
173 # with user creation, explanation should be provided in
173 # Exception itself
174 # Exception itself
174 session.flash(e, queue='error')
175 session.flash(e, queue='error')
175 return self._get_template_context()
176 return self._get_template_context()
176
177
177 @CSRFRequired()
178 @CSRFRequired()
178 @view_config(route_name='logout', request_method='POST')
179 @view_config(route_name='logout', request_method='POST')
179 def logout(self):
180 def logout(self):
180 LoginSession().destroy_user_session()
181 LoginSession().destroy_user_session()
181 return HTTPFound(url('home'))
182 return HTTPFound(url('home'))
182
183
183 @HasPermissionAnyDecorator(
184 @HasPermissionAnyDecorator(
184 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
185 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
185 @view_config(
186 @view_config(
186 route_name='register', request_method='GET',
187 route_name='register', request_method='GET',
187 renderer='rhodecode:templates/register.html',)
188 renderer='rhodecode:templates/register.html',)
188 def register(self, defaults=None, errors=None):
189 def register(self, defaults=None, errors=None):
189 defaults = defaults or {}
190 defaults = defaults or {}
190 errors = errors or {}
191 errors = errors or {}
191
192
192 settings = SettingsModel().get_all_settings()
193 settings = SettingsModel().get_all_settings()
193 captcha_public_key = settings.get('rhodecode_captcha_public_key')
194 captcha_public_key = settings.get('rhodecode_captcha_public_key')
194 captcha_private_key = settings.get('rhodecode_captcha_private_key')
195 captcha_private_key = settings.get('rhodecode_captcha_private_key')
195 captcha_active = bool(captcha_private_key)
196 captcha_active = bool(captcha_private_key)
196 register_message = settings.get('rhodecode_register_message') or ''
197 register_message = settings.get('rhodecode_register_message') or ''
197 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
198 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
198 .AuthUser.permissions['global']
199 .AuthUser.permissions['global']
199
200
200 render_ctx = self._get_template_context()
201 render_ctx = self._get_template_context()
201 render_ctx.update({
202 render_ctx.update({
202 'defaults': defaults,
203 'defaults': defaults,
203 'errors': errors,
204 'errors': errors,
204 'auto_active': auto_active,
205 'auto_active': auto_active,
205 'captcha_active': captcha_active,
206 'captcha_active': captcha_active,
206 'captcha_public_key': captcha_public_key,
207 'captcha_public_key': captcha_public_key,
207 'register_message': register_message,
208 'register_message': register_message,
208 })
209 })
209 return render_ctx
210 return render_ctx
210
211
211 @HasPermissionAnyDecorator(
212 @HasPermissionAnyDecorator(
212 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
213 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
213 @view_config(
214 @view_config(
214 route_name='register', request_method='POST',
215 route_name='register', request_method='POST',
215 renderer='rhodecode:templates/register.html')
216 renderer='rhodecode:templates/register.html')
216 def register_post(self):
217 def register_post(self):
217 captcha_private_key = SettingsModel().get_setting_by_name(
218 captcha_private_key = SettingsModel().get_setting_by_name(
218 'rhodecode_captcha_private_key')
219 'rhodecode_captcha_private_key')
219 captcha_active = bool(captcha_private_key)
220 captcha_active = bool(captcha_private_key)
220 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
221 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
221 .AuthUser.permissions['global']
222 .AuthUser.permissions['global']
222
223
223 register_form = RegisterForm()()
224 register_form = RegisterForm()()
224 try:
225 try:
225 form_result = register_form.to_python(self.request.params)
226 form_result = register_form.to_python(self.request.params)
226 form_result['active'] = auto_active
227 form_result['active'] = auto_active
227
228
228 if captcha_active:
229 if captcha_active:
229 response = submit(
230 response = submit(
230 self.request.params.get('recaptcha_challenge_field'),
231 self.request.params.get('recaptcha_challenge_field'),
231 self.request.params.get('recaptcha_response_field'),
232 self.request.params.get('recaptcha_response_field'),
232 private_key=captcha_private_key,
233 private_key=captcha_private_key,
233 remoteip=get_ip_addr(self.request.environ))
234 remoteip=get_ip_addr(self.request.environ))
234 if captcha_active and not response.is_valid:
235 if captcha_active and not response.is_valid:
235 _value = form_result
236 _value = form_result
236 _msg = _('bad captcha')
237 _msg = _('bad captcha')
237 error_dict = {'recaptcha_field': _msg}
238 error_dict = {'recaptcha_field': _msg}
238 raise formencode.Invalid(_msg, _value, None,
239 raise formencode.Invalid(_msg, _value, None,
239 error_dict=error_dict)
240 error_dict=error_dict)
240
241
241 new_user = UserModel().create_registration(form_result)
242 new_user = UserModel().create_registration(form_result)
242 event = UserRegistered(user=new_user, session=self.session)
243 event = UserRegistered(user=new_user, session=self.session)
243 self.request.registry.notify(event)
244 self.request.registry.notify(event)
244 self.session.flash(
245 self.session.flash(
245 _('You have successfully registered with RhodeCode'),
246 _('You have successfully registered with RhodeCode'),
246 queue='success')
247 queue='success')
247 Session().commit()
248 Session().commit()
248
249
249 redirect_ro = self.request.route_path('login')
250 redirect_ro = self.request.route_path('login')
250 raise HTTPFound(redirect_ro)
251 raise HTTPFound(redirect_ro)
251
252
252 except formencode.Invalid as errors:
253 except formencode.Invalid as errors:
253 del errors.value['password']
254 del errors.value['password']
254 del errors.value['password_confirmation']
255 del errors.value['password_confirmation']
255 return self.register(
256 return self.register(
256 defaults=errors.value, errors=errors.error_dict)
257 defaults=errors.value, errors=errors.error_dict)
257
258
258 except UserCreationError as e:
259 except UserCreationError as e:
259 # container auth or other auth functions that create users on
260 # container auth or other auth functions that create users on
260 # the fly can throw this exception signaling that there's issue
261 # the fly can throw this exception signaling that there's issue
261 # with user creation, explanation should be provided in
262 # with user creation, explanation should be provided in
262 # Exception itself
263 # Exception itself
263 self.session.flash(e, queue='error')
264 self.session.flash(e, queue='error')
264 return self.register()
265 return self.register()
265
266
266 @view_config(
267 @view_config(
267 route_name='reset_password', request_method=('GET', 'POST'),
268 route_name='reset_password', request_method=('GET', 'POST'),
268 renderer='rhodecode:templates/password_reset.html')
269 renderer='rhodecode:templates/password_reset.html')
269 def password_reset(self):
270 def password_reset(self):
270 settings = SettingsModel().get_all_settings()
271 settings = SettingsModel().get_all_settings()
271 captcha_private_key = settings.get('rhodecode_captcha_private_key')
272 captcha_private_key = settings.get('rhodecode_captcha_private_key')
272 captcha_active = bool(captcha_private_key)
273 captcha_active = bool(captcha_private_key)
273 captcha_public_key = settings.get('rhodecode_captcha_public_key')
274 captcha_public_key = settings.get('rhodecode_captcha_public_key')
274
275
275 render_ctx = {
276 render_ctx = {
276 'captcha_active': captcha_active,
277 'captcha_active': captcha_active,
277 'captcha_public_key': captcha_public_key,
278 'captcha_public_key': captcha_public_key,
278 'defaults': {},
279 'defaults': {},
279 'errors': {},
280 'errors': {},
280 }
281 }
281
282
282 if self.request.POST:
283 if self.request.POST:
283 password_reset_form = PasswordResetForm()()
284 password_reset_form = PasswordResetForm()()
284 try:
285 try:
285 form_result = password_reset_form.to_python(
286 form_result = password_reset_form.to_python(
286 self.request.params)
287 self.request.params)
287 if captcha_active:
288 if captcha_active:
288 response = submit(
289 response = submit(
289 self.request.params.get('recaptcha_challenge_field'),
290 self.request.params.get('recaptcha_challenge_field'),
290 self.request.params.get('recaptcha_response_field'),
291 self.request.params.get('recaptcha_response_field'),
291 private_key=captcha_private_key,
292 private_key=captcha_private_key,
292 remoteip=get_ip_addr(self.request.environ))
293 remoteip=get_ip_addr(self.request.environ))
293 if captcha_active and not response.is_valid:
294 if captcha_active and not response.is_valid:
294 _value = form_result
295 _value = form_result
295 _msg = _('bad captcha')
296 _msg = _('bad captcha')
296 error_dict = {'recaptcha_field': _msg}
297 error_dict = {'recaptcha_field': _msg}
297 raise formencode.Invalid(_msg, _value, None,
298 raise formencode.Invalid(_msg, _value, None,
298 error_dict=error_dict)
299 error_dict=error_dict)
299
300
300 # Generate reset URL and send mail.
301 # Generate reset URL and send mail.
301 user_email = form_result['email']
302 user_email = form_result['email']
302 user = User.get_by_email(user_email)
303 user = User.get_by_email(user_email)
303 password_reset_url = self.request.route_url(
304 password_reset_url = self.request.route_url(
304 'reset_password_confirmation',
305 'reset_password_confirmation',
305 _query={'key': user.api_key})
306 _query={'key': user.api_key})
306 UserModel().reset_password_link(
307 UserModel().reset_password_link(
307 form_result, password_reset_url)
308 form_result, password_reset_url)
308
309
309 # Display success message and redirect.
310 # Display success message and redirect.
310 self.session.flash(
311 self.session.flash(
311 _('Your password reset link was sent'),
312 _('Your password reset link was sent'),
312 queue='success')
313 queue='success')
313 return HTTPFound(self.request.route_path('login'))
314 return HTTPFound(self.request.route_path('login'))
314
315
315 except formencode.Invalid as errors:
316 except formencode.Invalid as errors:
316 render_ctx.update({
317 render_ctx.update({
317 'defaults': errors.value,
318 'defaults': errors.value,
318 'errors': errors.error_dict,
319 'errors': errors.error_dict,
319 })
320 })
320
321
321 return render_ctx
322 return render_ctx
322
323
323 @view_config(route_name='reset_password_confirmation',
324 @view_config(route_name='reset_password_confirmation',
324 request_method='GET')
325 request_method='GET')
325 def password_reset_confirmation(self):
326 def password_reset_confirmation(self):
326 if self.request.GET and self.request.GET.get('key'):
327 if self.request.GET and self.request.GET.get('key'):
327 try:
328 try:
328 user = User.get_by_auth_token(self.request.GET.get('key'))
329 user = User.get_by_auth_token(self.request.GET.get('key'))
329 data = {'email': user.email}
330 data = {'email': user.email}
330 UserModel().reset_password(data)
331 UserModel().reset_password(data)
331 self.session.flash(
332 self.session.flash(
332 _('Your password reset was successful, '
333 _('Your password reset was successful, '
333 'a new password has been sent to your email'),
334 'a new password has been sent to your email'),
334 queue='success')
335 queue='success')
335 except Exception as e:
336 except Exception as e:
336 log.error(e)
337 log.error(e)
337 return HTTPFound(self.request.route_path('reset_password'))
338 return HTTPFound(self.request.route_path('reset_password'))
338
339
339 return HTTPFound(self.request.route_path('login'))
340 return HTTPFound(self.request.route_path('login'))
General Comments 0
You need to be logged in to leave comments. Login now