##// END OF EJS Templates
login: Bring recaptcha back to work....
johbo -
r46:4bc0185d default
parent child Browse files
Show More
@@ -1,354 +1,346 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 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 def register(self):
200 def register(self, defaults={}, errors={}):
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 'defaults': defaults,
212 'errors': errors,
211 213 'auto_active': auto_active,
212 214 'captcha_active': captcha_active,
213 215 'captcha_public_key': captcha_public_key,
214 216 'register_message': register_message,
215 217 })
216 218 return render_ctx
217 219
218 220 @view_config(
219 221 route_name='register', request_method='POST',
220 222 renderer='rhodecode:templates/register.html')
221 223 def register_post(self):
222 settings = SettingsModel().get_all_settings()
223 captcha_private_key = settings.get('rhodecode_captcha_private_key')
224 captcha_private_key = SettingsModel().get_setting_by_name(
225 'rhodecode_captcha_private_key')
224 226 captcha_active = bool(captcha_private_key)
225 register_message = settings.get('rhodecode_register_message') or ''
226 227 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
227 228 .AuthUser.permissions['global']
228 229
229 230 register_form = RegisterForm()()
230 231 try:
231 232 form_result = register_form.to_python(self.request.params)
232 233 form_result['active'] = auto_active
233 234
234 235 if captcha_active:
235 236 response = submit(
236 237 self.request.params.get('recaptcha_challenge_field'),
237 238 self.request.params.get('recaptcha_response_field'),
238 239 private_key=captcha_private_key,
239 240 remoteip=get_ip_addr(self.request.environ))
240 241 if captcha_active and not response.is_valid:
241 242 _value = form_result
242 243 _msg = _('bad captcha')
243 244 error_dict = {'recaptcha_field': _msg}
244 245 raise formencode.Invalid(_msg, _value, None,
245 246 error_dict=error_dict)
246 247
247 248 new_user = UserModel().create_registration(form_result)
248 249 event = UserRegistered(user=new_user, session=self.session)
249 250 self.request.registry.notify(event)
250 251 self.session.flash(
251 252 _('You have successfully registered with RhodeCode'),
252 253 queue='success')
253 254 Session().commit()
254 255
255 256 redirect_ro = self.request.route_path('login')
256 257 raise HTTPFound(redirect_ro)
257 258
258 259 except formencode.Invalid as errors:
259 260 del errors.value['password']
260 261 del errors.value['password_confirmation']
261 render_ctx = self._get_template_context()
262 render_ctx.update({
263 'errors': errors.error_dict,
264 'defaults': errors.value,
265 'register_message': register_message,
266 })
267 return render_ctx
262 return self.register(
263 defaults=errors.value, errors=errors.error_dict)
268 264
269 265 except UserCreationError as e:
270 266 # container auth or other auth functions that create users on
271 267 # the fly can throw this exception signaling that there's issue
272 268 # with user creation, explanation should be provided in
273 269 # Exception itself
274 270 self.session.flash(e, queue='error')
275 render_ctx = self._get_template_context()
276 render_ctx.update({
277 'register_message': register_message,
278 })
279 return render_ctx
271 return self.register()
280 272
281 273 @view_config(
282 274 route_name='reset_password', request_method=('GET', 'POST'),
283 275 renderer='rhodecode:templates/password_reset.html')
284 276 def password_reset(self):
285 277 settings = SettingsModel().get_all_settings()
286 278 captcha_private_key = settings.get('rhodecode_captcha_private_key')
287 279 captcha_active = bool(captcha_private_key)
288 280 captcha_public_key = settings.get('rhodecode_captcha_public_key')
289 281
290 282 render_ctx = {
291 283 'captcha_active': captcha_active,
292 284 'captcha_public_key': captcha_public_key,
293 285 'defaults': {},
294 286 'errors': {},
295 287 }
296 288
297 289 if self.request.POST:
298 290 password_reset_form = PasswordResetForm()()
299 291 try:
300 292 form_result = password_reset_form.to_python(
301 293 self.request.params)
302 294 if captcha_active:
303 295 response = submit(
304 296 self.request.params.get('recaptcha_challenge_field'),
305 297 self.request.params.get('recaptcha_response_field'),
306 298 private_key=captcha_private_key,
307 299 remoteip=get_ip_addr(self.request.environ))
308 300 if captcha_active and not response.is_valid:
309 301 _value = form_result
310 302 _msg = _('bad captcha')
311 303 error_dict = {'recaptcha_field': _msg}
312 304 raise formencode.Invalid(_msg, _value, None,
313 305 error_dict=error_dict)
314 306
315 307 # Generate reset URL and send mail.
316 308 user_email = form_result['email']
317 309 user = User.get_by_email(user_email)
318 310 password_reset_url = self.request.route_url(
319 311 'reset_password_confirmation',
320 312 _query={'key': user.api_key})
321 313 UserModel().reset_password_link(
322 314 form_result, password_reset_url)
323 315
324 316 # Display success message and redirect.
325 317 self.session.flash(
326 318 _('Your password reset link was sent'),
327 319 queue='success')
328 320 return HTTPFound(self.request.route_path('login'))
329 321
330 322 except formencode.Invalid as errors:
331 323 render_ctx.update({
332 324 'defaults': errors.value,
333 325 'errors': errors.error_dict,
334 326 })
335 327
336 328 return render_ctx
337 329
338 330 @view_config(route_name='reset_password_confirmation',
339 331 request_method='GET')
340 332 def password_reset_confirmation(self):
341 333 if self.request.GET and self.request.GET.get('key'):
342 334 try:
343 335 user = User.get_by_auth_token(self.request.GET.get('key'))
344 336 data = {'email': user.email}
345 337 UserModel().reset_password(data)
346 338 self.session.flash(
347 339 _('Your password reset was successful, '
348 340 'a new password has been sent to your email'),
349 341 queue='success')
350 342 except Exception as e:
351 343 log.error(e)
352 344 return HTTPFound(self.request.route_path('reset_password'))
353 345
354 346 return HTTPFound(self.request.route_path('login'))
General Comments 0
You need to be logged in to leave comments. Login now