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