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