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