##// END OF EJS Templates
login: Remove social auth code from login controller....
johbo -
r23:dd346b2b default
parent child Browse files
Show More
@@ -1,409 +1,290 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-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 """
22 22 Login controller for rhodeocode
23 23 """
24 24
25 25 import datetime
26 26 import formencode
27 27 import logging
28 28 import urlparse
29 29 import uuid
30 30
31 31 from formencode import htmlfill
32 32 from webob.exc import HTTPFound
33 33 from pylons.i18n.translation import _
34 34 from pylons.controllers.util import redirect
35 35 from pylons import request, session, tmpl_context as c, url
36 36 from recaptcha.client.captcha import submit
37 37
38 38 import rhodecode.lib.helpers as h
39 39 from rhodecode.lib.auth import (
40 40 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
41 41 from rhodecode.authentication.base import loadplugin
42 42 from rhodecode.lib.base import BaseController, render
43 43 from rhodecode.lib.exceptions import UserCreationError
44 44 from rhodecode.lib.utils2 import safe_str
45 from rhodecode.model.db import User, ExternalIdentity
45 from rhodecode.model.db import User
46 46 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
47 47 from rhodecode.model.login_session import LoginSession
48 48 from rhodecode.model.meta import Session
49 49 from rhodecode.model.settings import SettingsModel
50 50 from rhodecode.model.user import UserModel
51 51
52 52 log = logging.getLogger(__name__)
53 53
54 54
55 55 class LoginController(BaseController):
56 56
57 57 def __before__(self):
58 58 super(LoginController, self).__before__()
59 59
60 60 def _store_user_in_session(self, username, remember=False):
61 61 user = User.get_by_username(username, case_insensitive=True)
62 62 auth_user = AuthUser(user.user_id)
63 63 auth_user.set_authenticated()
64 64 cs = auth_user.get_cookie_store()
65 65 session['rhodecode_user'] = cs
66 66 user.update_lastlogin()
67 67 Session().commit()
68 68
69 69 # If they want to be remembered, update the cookie
70 70 if remember:
71 71 _year = (datetime.datetime.now() +
72 72 datetime.timedelta(seconds=60 * 60 * 24 * 365))
73 73 session._set_cookie_expires(_year)
74 74
75 75 session.save()
76 76
77 77 log.info('user %s is now authenticated and stored in '
78 78 'session, session attrs %s', username, cs)
79 79
80 80 # dumps session attrs back to cookie
81 81 session._update_cookie_out()
82 82 # we set new cookie
83 83 headers = None
84 84 if session.request['set_cookie']:
85 85 # send set-cookie headers back to response to update cookie
86 86 headers = [('Set-Cookie', session.request['cookie_out'])]
87 87 return headers
88 88
89 89 def _validate_came_from(self, came_from):
90 90 if not came_from:
91 91 return came_from
92 92
93 93 parsed = urlparse.urlparse(came_from)
94 94 server_parsed = urlparse.urlparse(url.current())
95 95 allowed_schemes = ['http', 'https']
96 96 if parsed.scheme and parsed.scheme not in allowed_schemes:
97 97 log.error('Suspicious URL scheme detected %s for url %s' %
98 98 (parsed.scheme, parsed))
99 99 came_from = url('home')
100 100 elif server_parsed.netloc != parsed.netloc:
101 101 log.error('Suspicious NETLOC detected %s for url %s server url '
102 102 'is: %s' % (parsed.netloc, parsed, server_parsed))
103 103 came_from = url('home')
104 104 if any(bad_str in parsed.path for bad_str in ('\r', '\n')):
105 105 log.error('Header injection detected `%s` for url %s server url ' %
106 106 (parsed.path, parsed))
107 107 came_from = url('home')
108 108 return came_from
109 109
110 110 def _redirect_to_origin(self, location, headers=None):
111 111 request.GET.pop('came_from', None)
112 112 raise HTTPFound(location=location, headers=headers)
113 113
114 114 def _set_came_from(self):
115 115 _default_came_from = url('home')
116 116 came_from = self._validate_came_from(
117 117 safe_str(request.GET.get('came_from', '')))
118 118 c.came_from = came_from or _default_came_from
119 119
120 120 def index(self):
121 121 self._set_came_from()
122 122
123 123 not_default = c.rhodecode_user.username != User.DEFAULT_USER
124 124 ip_allowed = c.rhodecode_user.ip_allowed
125 c.social_plugins = self._get_active_social_plugins()
126 125
127 126 # redirect if already logged in
128 127 if c.rhodecode_user.is_authenticated and not_default and ip_allowed:
129 128 raise self._redirect_to_origin(location=c.came_from)
130 129
131 130 if request.POST:
132 131 # import Login Form validator class
133 132 login_form = LoginForm()()
134 133 try:
135 134 session.invalidate()
136 135 c.form_result = login_form.to_python(dict(request.POST))
137 136 # form checks for username/password, now we're authenticated
138 137 headers = self._store_user_in_session(
139 138 username=c.form_result['username'],
140 139 remember=c.form_result['remember'])
141 140 raise self._redirect_to_origin(
142 141 location=c.came_from, headers=headers)
143 142 except formencode.Invalid as errors:
144 143 defaults = errors.value
145 144 # remove password from filling in form again
146 145 del defaults['password']
147 146 return htmlfill.render(
148 147 render('/login.html'),
149 148 defaults=errors.value,
150 149 errors=errors.error_dict or {},
151 150 prefix_error=False,
152 151 encoding="UTF-8",
153 152 force_defaults=False)
154 153 except UserCreationError as e:
155 154 # container auth or other auth functions that create users on
156 155 # the fly can throw this exception signaling that there's issue
157 156 # with user creation, explanation should be provided in
158 157 # Exception itself
159 158 h.flash(e, 'error')
160 159
161 160 # check if we use container plugin, and try to login using it.
162 161 from rhodecode.authentication.base import authenticate, HTTP_TYPE
163 162 try:
164 163 log.debug('Running PRE-AUTH for container based authentication')
165 164 auth_info = authenticate(
166 165 '', '', request.environ, HTTP_TYPE, skip_missing=True)
167 166 except UserCreationError as e:
168 167 log.error(e)
169 168 h.flash(e, 'error')
170 169 # render login, with flash message about limit
171 170 return render('/login.html')
172 171
173 172 if auth_info:
174 173 headers = self._store_user_in_session(auth_info.get('username'))
175 174 raise self._redirect_to_origin(
176 175 location=c.came_from, headers=headers)
177 176 return render('/login.html')
178 177
179 # TODO: Move this to a better place.
180 def _get_active_social_plugins(self):
181 from rhodecode.authentication.base import AuthomaticBase
182 activated_plugins = SettingsModel().get_auth_plugins()
183 social_plugins = []
184 for plugin_id in activated_plugins:
185 plugin = loadplugin(plugin_id)
186 if isinstance(plugin, AuthomaticBase) and plugin.is_active():
187 social_plugins.append(plugin)
188 return social_plugins
189
190 178 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
191 179 'hg.register.manual_activate')
192 180 def register(self):
193 181 c.auto_active = 'hg.register.auto_activate' in User.get_default_user()\
194 182 .AuthUser.permissions['global']
195 183
196 184 settings = SettingsModel().get_all_settings()
197 185 captcha_private_key = settings.get('rhodecode_captcha_private_key')
198 186 c.captcha_active = bool(captcha_private_key)
199 187 c.captcha_public_key = settings.get('rhodecode_captcha_public_key')
200 188 c.register_message = settings.get('rhodecode_register_message') or ''
201
202 c.social_plugins = self._get_active_social_plugins()
203
204 social_data = session.get('rhodecode.social_auth')
205 189 c.form_data = {}
206 if social_data:
207 c.form_data = {'username': social_data['user'].get('user_name'),
208 'password': str(uuid.uuid4()),
209 'email': social_data['user'].get('email')
210 }
211 190
212 191 if request.POST:
213 192 register_form = RegisterForm()()
214 193 try:
215 194 form_result = register_form.to_python(dict(request.POST))
216 195 form_result['active'] = c.auto_active
217 196
218 197 if c.captcha_active:
219 198 response = submit(
220 199 request.POST.get('recaptcha_challenge_field'),
221 200 request.POST.get('recaptcha_response_field'),
222 201 private_key=captcha_private_key,
223 202 remoteip=self.ip_addr)
224 203 if c.captcha_active and not response.is_valid:
225 204 _value = form_result
226 205 _msg = _('bad captcha')
227 206 error_dict = {'recaptcha_field': _msg}
228 207 raise formencode.Invalid(_msg, _value, None,
229 208 error_dict=error_dict)
230 209
231 new_user = UserModel().create_registration(form_result)
232 if social_data:
233 plugin_name = 'egg:rhodecode-enterprise-ee#{}'.format(
234 social_data['credentials.provider']
235 )
236 auth_plugin = loadplugin(plugin_name)
237 if auth_plugin:
238 auth_plugin.handle_social_data(
239 session, new_user.user_id, social_data)
210 UserModel().create_registration(form_result)
240 211 h.flash(_('You have successfully registered with RhodeCode'),
241 212 category='success')
242 213 Session().commit()
243 214 return redirect(url('login_home'))
244 215
245 216 except formencode.Invalid as errors:
246 217 return htmlfill.render(
247 218 render('/register.html'),
248 219 defaults=errors.value,
249 220 errors=errors.error_dict or {},
250 221 prefix_error=False,
251 222 encoding="UTF-8",
252 223 force_defaults=False)
253 224 except UserCreationError as e:
254 225 # container auth or other auth functions that create users on
255 226 # the fly can throw this exception signaling that there's issue
256 227 # with user creation, explanation should be provided in
257 228 # Exception itself
258 229 h.flash(e, 'error')
259 230
260 231 return render('/register.html')
261 232
262 233 def password_reset(self):
263 234 settings = SettingsModel().get_all_settings()
264 235 captcha_private_key = settings.get('rhodecode_captcha_private_key')
265 236 c.captcha_active = bool(captcha_private_key)
266 237 c.captcha_public_key = settings.get('rhodecode_captcha_public_key')
267 238
268 239 if request.POST:
269 240 password_reset_form = PasswordResetForm()()
270 241 try:
271 242 form_result = password_reset_form.to_python(dict(request.POST))
272 243 if c.captcha_active:
273 244 response = submit(
274 245 request.POST.get('recaptcha_challenge_field'),
275 246 request.POST.get('recaptcha_response_field'),
276 247 private_key=captcha_private_key,
277 248 remoteip=self.ip_addr)
278 249 if c.captcha_active and not response.is_valid:
279 250 _value = form_result
280 251 _msg = _('bad captcha')
281 252 error_dict = {'recaptcha_field': _msg}
282 253 raise formencode.Invalid(_msg, _value, None,
283 254 error_dict=error_dict)
284 255 UserModel().reset_password_link(form_result)
285 256 h.flash(_('Your password reset link was sent'),
286 257 category='success')
287 258 return redirect(url('login_home'))
288 259
289 260 except formencode.Invalid as errors:
290 261 return htmlfill.render(
291 262 render('/password_reset.html'),
292 263 defaults=errors.value,
293 264 errors=errors.error_dict or {},
294 265 prefix_error=False,
295 266 encoding="UTF-8",
296 267 force_defaults=False)
297 268
298 269 return render('/password_reset.html')
299 270
300 271 def password_reset_confirmation(self):
301 272 if request.GET and request.GET.get('key'):
302 273 try:
303 274 user = User.get_by_auth_token(request.GET.get('key'))
304 275 data = {'email': user.email}
305 276 UserModel().reset_password(data)
306 277 h.flash(_(
307 278 'Your password reset was successful, '
308 279 'a new password has been sent to your email'),
309 280 category='success')
310 281 except Exception as e:
311 282 log.error(e)
312 283 return redirect(url('reset_password'))
313 284
314 285 return redirect(url('login_home'))
315 286
316 287 @CSRFRequired()
317 288 def logout(self):
318 289 LoginSession().destroy_user_session()
319 290 return redirect(url('home'))
320
321 def social_auth(self, provider_name):
322 plugin_name = 'egg:rhodecode-enterprise-ee#{}'.format(
323 provider_name
324 )
325 auth_plugin = loadplugin(plugin_name)
326 if not auth_plugin:
327 return self._handle_social_auth_error(request, 'No auth plugin')
328
329 result, response = auth_plugin.get_provider_result(request)
330 if result:
331 if result.error:
332 return self._handle_social_auth_error(request, result.error)
333 elif result.user:
334 return self._handle_social_auth_success(request, result)
335 return response
336
337 def _handle_social_auth_error(self, request, result):
338 log.error(result)
339 h.flash(_('There was an error during OAuth processing.'),
340 category='error')
341 return redirect(url('home'))
342
343 def _normalize_social_data(self, result):
344 social_data = {
345 'user': {'data': result.user.data},
346 'credentials.provider': result.user.credentials.provider_name,
347 'credentials.token': result.user.credentials.token,
348 'credentials.token_secret': result.user.credentials.token_secret,
349 'credentials.refresh_token': result.user.credentials.refresh_token
350 }
351 # normalize data
352 social_data['user']['id'] = result.user.id
353 user_name = result.user.username or ''
354 # use email name as username for google
355 if (social_data['credentials.provider'] == 'google' and
356 result.user.email):
357 user_name = result.user.email
358
359 social_data['user']['user_name'] = user_name
360 social_data['user']['email'] = result.user.email or ''
361 return social_data
362
363 def _handle_social_auth_success(self, request, result):
364 self._set_came_from()
365
366 # Hooray, we have the user!
367 # OAuth 2.0 and OAuth 1.0a provide only limited user data on login,
368 # We need to update the user to get more info.
369 if result.user:
370 result.user.update()
371
372 social_data = self._normalize_social_data(result)
373
374 session['rhodecode.social_auth'] = social_data
375
376 plugin_name = 'egg:rhodecode-enterprise-ee#{}'.format(
377 social_data['credentials.provider']
378 )
379 auth_plugin = loadplugin(plugin_name)
380
381 # user is logged so bind his external identity with account
382 if request.user and request.user.username != User.DEFAULT_USER:
383 if auth_plugin:
384 auth_plugin.handle_social_data(
385 session, request.user.user_id, social_data)
386 session.pop('rhodecode.social_auth', None)
387 Session().commit()
388 return redirect(url('my_account_oauth'))
389 else:
390 user = ExternalIdentity.user_by_external_id_and_provider(
391 social_data['user']['id'],
392 social_data['credentials.provider']
393 )
394
395 # user tokens are already found in our db
396 if user:
397 if auth_plugin:
398 auth_plugin.handle_social_data(
399 session, user.user_id, social_data)
400 session.pop('rhodecode.social_auth', None)
401 headers = self._store_user_in_session(user.username)
402 raise self._redirect_to_origin(
403 location=c.came_from, headers=headers)
404 else:
405 msg = _('You need to finish registration '
406 'process to bind your external identity to your '
407 'account or sign in to existing account')
408 h.flash(msg, category='success')
409 return redirect(url('register'))
General Comments 0
You need to be logged in to leave comments. Login now