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