PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/socialregistration/views.py

http://github.com/flashingpumpkin/django-socialregistration
Python | 348 lines | 216 code | 64 blank | 68 comment | 20 complexity | 35747b9792d1a440fd950fcaef3a17cd MD5 | raw file
  1. from django.conf import settings
  2. from django.contrib.auth import logout
  3. from django.core.urlresolvers import reverse
  4. from django.http import HttpResponseRedirect
  5. from django.utils.translation import ugettext_lazy as _
  6. from django.views.generic.base import View, TemplateView
  7. from socialregistration.clients.oauth import OAuthError
  8. from socialregistration.contrib.openid.client import OpenIDClient
  9. from socialregistration.mixins import SocialRegistration
  10. import logging
  11. import socket
  12. GENERATE_USERNAME = getattr(settings, 'SOCIALREGISTRATION_GENERATE_USERNAME', False)
  13. USERNAME_FUNCTION = getattr(settings, 'SOCIALREGISTRATION_GENERATE_USERNAME_FUNCTION',
  14. 'socialregistration.utils.generate_username')
  15. FORM_CLASS = getattr(settings, 'SOCIALREGISTRATION_SETUP_FORM',
  16. 'socialregistration.forms.UserForm')
  17. INITAL_DATA_FUNCTION = getattr(settings, 'SOCIALREGISTRATION_INITIAL_DATA_FUNCTION',
  18. None)
  19. CONTEXT_FUNCTION = getattr(settings, 'SOCIALREGISTRATION_SETUP_CONTEXT_FUNCTION',
  20. None)
  21. ALLOW_OPENID_SIGNUPS = getattr(settings, 'SOCIALREGISTRATION_ALLOW_OPENID_SIGNUPS',
  22. True)
  23. logger = logging.getLogger(__name__)
  24. class Setup(SocialRegistration, View):
  25. """
  26. Setup view to create new Django users from third party APIs.
  27. """
  28. template_name = 'socialregistration/setup.html'
  29. def get_form(self):
  30. """
  31. Return the form to be used. The return form is controlled
  32. with ``SOCIALREGISTRATION_SETUP_FORM``.
  33. """
  34. return self.import_attribute(FORM_CLASS)
  35. def get_username_function(self):
  36. """
  37. Return a function that can generate a username. The function
  38. is controlled with ``SOCIALREGISTRATION_GENERATE_USERNAME_FUNCTION``.
  39. """
  40. return self.import_attribute(USERNAME_FUNCTION)
  41. def get_initial_data(self, request, user, profile, client):
  42. """
  43. Return initial data for the setup form. The function can be
  44. controlled with ``SOCIALREGISTRATION_INITIAL_DATA_FUNCTION``.
  45. :param request: The current request object
  46. :param user: The unsaved user object
  47. :param profile: The unsaved profile object
  48. :param client: The API client
  49. """
  50. if INITAL_DATA_FUNCTION:
  51. func = self.import_attribute(INITAL_DATA_FUNCTION)
  52. return func(request, user, profile, client)
  53. return {}
  54. def get_context(self, request, user, profile, client):
  55. """
  56. Return additional context for the setup view. The function can
  57. be controlled with ``SOCIALREGISTRATION_SETUP_CONTEXT_FUNCTION``.
  58. :param request: The current request object
  59. :param user: The unsaved user object
  60. :param profile: The unsaved profile object
  61. :param client: The API client
  62. """
  63. if CONTEXT_FUNCTION:
  64. func = self.import_attribute(CONTEXT_FUNCTION)
  65. return func(request, user, profile, client)
  66. return {}
  67. def generate_username_and_redirect(self, request, user, profile, client):
  68. """
  69. Generate a username and then redirect the user to the correct place.
  70. This method is called when ``SOCIALREGISTRATION_GENERATE_USERNAME``
  71. is set.
  72. :param request: The current request object
  73. :param user: The unsaved user object
  74. :param profile: The unsaved profile object
  75. :param client: The API client
  76. """
  77. func = self.get_username_function()
  78. user.username = func(user, profile, client)
  79. user.set_unusable_password()
  80. user.save()
  81. profile.user = user
  82. profile.save()
  83. user = profile.authenticate()
  84. self.send_connect_signal(request, user, profile, client)
  85. self.login(request, user)
  86. self.send_login_signal(request, user, profile, client)
  87. self.delete_session_data(request)
  88. return HttpResponseRedirect(self.get_next(request))
  89. def get(self, request):
  90. """
  91. When signing a new user up - either display a setup form, or
  92. generate the username automatically.
  93. """
  94. if request.user.is_authenticated():
  95. return HttpResponseRedirect(self.get_next(request))
  96. try:
  97. user, profile, client = self.get_session_data(request)
  98. except KeyError:
  99. return self.error_to_response(request, dict(
  100. error=_("Social profile is missing from your session.")))
  101. if GENERATE_USERNAME:
  102. return self.generate_username_and_redirect(request, user, profile, client)
  103. form = self.get_form()(initial=self.get_initial_data(request, user, profile, client))
  104. additional_context = self.get_context(request, user, profile, client)
  105. return self.render_to_response(dict({'form': form}, **additional_context))
  106. def post(self, request):
  107. """
  108. Save the user and profile, login and send the right signals.
  109. """
  110. if request.user.is_authenticated():
  111. return self.error_to_response(request, dict(
  112. error=_("You are already logged in.")))
  113. try:
  114. user, profile, client = self.get_session_data(request)
  115. except KeyError:
  116. return self.error_to_response(request, dict(
  117. error=_("A social profile is missing from your session.")))
  118. form = self.get_form()(request.POST, request.FILES,
  119. initial=self.get_initial_data(request, user, profile, client))
  120. if not form.is_valid():
  121. additional_context = self.get_context(request, user, profile, client)
  122. return self.render_to_response(dict({'form': form}, **additional_context))
  123. user, profile = form.save(request, user, profile, client)
  124. user = profile.authenticate()
  125. self.send_connect_signal(request, user, profile, client)
  126. self.login(request, user)
  127. self.send_login_signal(request, user, profile, client)
  128. self.delete_session_data(request)
  129. return HttpResponseRedirect(self.get_next(request))
  130. class Logout(View):
  131. """
  132. Log the user out of Django. This **does not** log the user out
  133. of third party sites.
  134. """
  135. def get(self, request):
  136. logout(request)
  137. url = getattr(settings, 'LOGOUT_REDIRECT_URL', '/')
  138. return HttpResponseRedirect(url)
  139. class OAuthRedirect(SocialRegistration, View):
  140. """
  141. Base class for both OAuth and OAuth2 redirects.
  142. :param client: The API client class that should be used.
  143. :param template_name: The error template.
  144. """
  145. # The OAuth{1,2} client to be used
  146. client = None
  147. # The template to render in case of errors
  148. template_name = None
  149. def post(self, request):
  150. """
  151. Create a client, store it in the user's session and redirect the user
  152. to the API provider to authorize our app and permissions.
  153. """
  154. request.session['next'] = self.get_next(request)
  155. client = self.get_client()()
  156. request.session[self.get_client().get_session_key()] = client
  157. url = client.get_redirect_url(request=request)
  158. logger.debug("Redirecting to %s", url)
  159. try:
  160. return HttpResponseRedirect(url)
  161. except OAuthError, error:
  162. return self.error_to_response(request, {'error': error})
  163. except socket.timeout:
  164. return self.error_to_response(request, {'error':
  165. _('Could not connect to service (timed out)')})
  166. class OAuthCallback(SocialRegistration, View):
  167. """
  168. Base class for OAuth and OAuth2 callback views.
  169. :param client: The API client class that should be used.
  170. :param template_name: The error template.
  171. """
  172. # The OAuth{1,2} client to be used
  173. client = None
  174. # The template to render in case of errors
  175. template_name = None
  176. def get_redirect(self):
  177. """
  178. Return a URL that will set up the correct models if the
  179. OAuth flow succeeded. Subclasses **must** override this
  180. method.
  181. """
  182. raise NotImplementedError
  183. def get(self, request):
  184. """
  185. Called after the user is redirected back to our application.
  186. Tries to:
  187. - Complete the OAuth / OAuth2 flow
  188. - Redirect the user to another view that deals with login, connecting
  189. or user creation.
  190. """
  191. try:
  192. client = request.session[self.get_client().get_session_key()]
  193. logger.debug("API returned: %s", request.GET)
  194. client.complete(dict(request.GET.items()))
  195. request.session[self.get_client().get_session_key()] = client
  196. return HttpResponseRedirect(self.get_redirect())
  197. except KeyError:
  198. return self.error_to_response(request, {'error': "Session expired."})
  199. except OAuthError, error:
  200. return self.error_to_response(request, {'error': error})
  201. except socket.timeout:
  202. return self.error_to_response(request, {'error':
  203. _('Could not connect to service (timed out)')})
  204. class SetupCallback(SocialRegistration, TemplateView):
  205. """
  206. Base class for OAuth and OAuth2 login / connects / registration.
  207. """
  208. template_name = 'socialregistration/setup.error.html'
  209. def get(self, request):
  210. """
  211. Called after authorization was granted and the OAuth flow
  212. successfully completed.
  213. Tries to:
  214. - Connect the remote account if the user is logged in already
  215. - Log the user in if a local profile of the remote account
  216. exists already
  217. - Create a user and profile object if none of the above succeed
  218. and redirect the user further to either capture some data via
  219. form or generate a username automatically
  220. """
  221. try:
  222. client = request.session[self.get_client().get_session_key()]
  223. except KeyError:
  224. return self.error_to_response(request, {'error': "Session expired."})
  225. # Get the lookup dictionary to find the user's profile
  226. lookup_kwargs = self.get_lookup_kwargs(request, client)
  227. # Logged in user (re-)connecting an account
  228. if request.user.is_authenticated():
  229. try:
  230. profile = self.get_profile(**lookup_kwargs)
  231. # Make sure that there is only *one* account per profile.
  232. if not profile.user == request.user:
  233. self.delete_session_data(request)
  234. return self.error_to_response(request, {
  235. 'error': _('This profile is already connected to another user account.')
  236. })
  237. except self.get_model().DoesNotExist:
  238. profile, created = self.get_or_create_profile(request.user,
  239. save=True, **lookup_kwargs)
  240. self.send_connect_signal(request, request.user, profile, client)
  241. return self.redirect(request)
  242. # Logged out user - let's see if we've got the identity saved already.
  243. # If so - just log the user in. If not, create profile and redirect
  244. # to the setup view
  245. user = self.authenticate(**lookup_kwargs)
  246. # No user existing - create a new one and redirect to the final setup view
  247. if user is None:
  248. if not ALLOW_OPENID_SIGNUPS and self.client is OpenIDClient:
  249. return self.error_to_response(request, {
  250. 'error': _('We are not currently accepting new OpenID signups.')
  251. })
  252. user = self.create_user()
  253. profile = self.create_profile(user, **lookup_kwargs)
  254. self.store_user(request, user)
  255. self.store_profile(request, profile)
  256. self.store_client(request, client)
  257. return HttpResponseRedirect(reverse('socialregistration:setup'))
  258. # Inactive user - displaying / redirect to the appropriate place.
  259. if not user.is_active:
  260. return self.inactive_response(request)
  261. # Active user with existing profile: login, send signal and redirect
  262. self.login(request, user)
  263. profile = self.get_profile(user=user, **lookup_kwargs)
  264. self.send_login_signal(request, user, profile, client)
  265. return self.redirect(request)