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