PageRenderTime 23ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 0ms

/kuma/users/adapters.py

https://gitlab.com/ralt/kuma
Python | 150 lines | 97 code | 20 blank | 33 comment | 10 complexity | b0d6e792d159e1a36cd14af743b093a4 MD5 | raw file
  1. import re
  2. from django import forms
  3. from django.contrib import messages
  4. from django.contrib.auth.models import User
  5. from django.shortcuts import redirect
  6. from allauth.account.adapter import DefaultAccountAdapter, get_adapter
  7. from allauth.exceptions import ImmediateHttpResponse
  8. from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
  9. from allauth.socialaccount.models import SocialLogin
  10. from tower import ugettext_lazy as _
  11. from waffle import flag_is_active
  12. from kuma.core.urlresolvers import reverse
  13. from .constants import USERNAME_CHARACTERS, USERNAME_REGEX
  14. REMOVE_BUG_URL = "https://bugzilla.mozilla.org/enter_bug.cgi?assigned_to=nobody%40mozilla.org&bug_file_loc=http%3A%2F%2F&bug_ignored=0&bug_severity=normal&bug_status=NEW&cf_fx_iteration=---&cf_fx_points=---&comment=Please%20delete%20my%20MDN%20account.%20My%20username%20is%3A%0D%0A%0D%0A[username]&component=User%20management&contenttypemethod=autodetect&contenttypeselection=text%2Fplain&defined_groups=1&flag_type-4=X&flag_type-607=X&flag_type-791=X&flag_type-800=X&flag_type-803=X&form_name=enter_bug&maketemplate=Remember%20values%20as%20bookmarkable%20template&op_sys=All&priority=--&product=Mozilla%20Developer%20Network&rep_platform=All&short_desc=Account%20deletion%20request%20for%20[username]&status_whiteboard=[account-mod]&target_milestone=---&version=unspecified&format=__standard__"
  15. REMOVE_MESSAGE = _(u"Sorry, you must have at least one connected account so "
  16. u"you can sign in. To disconnect this account connect a "
  17. u"different one first. To delete your MDN profile please "
  18. u'<a href="%(bug_form_url)s" rel="nofollow">file a bug</a>.')
  19. USERNAME_EMAIL = _(u'An email address cannot be used as a username.')
  20. class KumaAccountAdapter(DefaultAccountAdapter):
  21. def is_open_for_signup(self, request):
  22. """
  23. We disable the signup with regular accounts as we require Persona
  24. (for now)
  25. """
  26. return False
  27. def clean_username(self, username):
  28. """
  29. When signing up make sure the username isn't already used by
  30. a different user, and doesn't contain invalid characters.
  31. """
  32. # We have stricter username requirements than django-allauth,
  33. # because we don't want to allow '@' in usernames. So we check
  34. # that before calling super() to make sure we catch those
  35. # problems and show our error messages.
  36. if '@' in username:
  37. raise forms.ValidationError(USERNAME_EMAIL)
  38. if not USERNAME_REGEX.match(username):
  39. raise forms.ValidationError(USERNAME_CHARACTERS)
  40. username = super(KumaAccountAdapter, self).clean_username(username)
  41. if User.objects.filter(username=username).exists():
  42. raise forms.ValidationError(_(u'The username you entered '
  43. u'already exists.'))
  44. return username
  45. def message_templates(self, *names):
  46. return tuple('messages/%s.txt' % name for name in names)
  47. def add_message(self, request, level, message_template,
  48. message_context={}, extra_tags='', *args, **kwargs):
  49. """
  50. Adds an extra "account" tag to the success and error messages.
  51. """
  52. # let's ignore some messages
  53. if message_template.endswith(self.message_templates('logged_in',
  54. 'logged_out')):
  55. return
  56. # promote the "account_connected" message to success
  57. if message_template.endswith(self.message_templates('account_connected')):
  58. level = messages.SUCCESS
  59. # when a next URL is set because of a multi step sign-in
  60. # (e.g. sign-in with github, verified mail is found in Persona
  61. # social accounts, agree to first log in with Persona to connect
  62. # instead) and the next URL is not the edit profile page (which
  63. # would indicate the start of the sign-in process from the edit
  64. # profile page) we ignore the message "account connected" message
  65. # as it would be misleading
  66. profile_url = reverse('users.profile_edit',
  67. kwargs={'username': request.user.username},
  68. locale=request.locale)
  69. next_url = request.session.get('sociallogin_next_url', None)
  70. if next_url != profile_url:
  71. return
  72. # and add an extra tag to the account messages
  73. extra_tag = 'account'
  74. if extra_tags:
  75. extra_tags += ' '
  76. extra_tags += extra_tag
  77. super(KumaAccountAdapter, self).add_message(request, level,
  78. message_template,
  79. message_context,
  80. extra_tags,
  81. *args, **kwargs)
  82. class KumaSocialAccountAdapter(DefaultSocialAccountAdapter):
  83. def is_open_for_signup(self, request, sociallogin):
  84. """
  85. We specifically enable social accounts as a way to signup
  86. because the default adapter uses the account adpater above
  87. as the default.
  88. """
  89. # Check if profile creation is disabled via waffle
  90. return not flag_is_active(request, 'registration_disabled')
  91. def validate_disconnect(self, account, accounts):
  92. """
  93. Validate whether or not the socialaccount account can be
  94. safely disconnected.
  95. """
  96. if len(accounts) == 1:
  97. raise forms.ValidationError(REMOVE_MESSAGE %
  98. {'bug_form_url': REMOVE_BUG_URL})
  99. def pre_social_login(self, request, sociallogin):
  100. """
  101. Invoked just after a user successfully authenticates via a
  102. social provider, but before the login is actually processed.
  103. We use it to:
  104. 1. Check if the user is connecting accounts via signup page
  105. 2. store the name of the socialaccount provider in the user's session.
  106. """
  107. session_login_data = request.session.get('socialaccount_sociallogin', None)
  108. request_login = sociallogin
  109. # Is there already a sociallogin_provider in the session?
  110. if session_login_data:
  111. session_login = SocialLogin.deserialize(session_login_data)
  112. # If the provider in the session is different from the provider in the
  113. # request, the user is connecting a new provider to an existing account
  114. if session_login.account.provider != request_login.account.provider:
  115. # Does the request sociallogin match an existing user?
  116. if not request_login.is_existing:
  117. # go straight back to signup page with an error message
  118. # BEFORE allauth over-writes the session sociallogin
  119. level = messages.ERROR
  120. message = "socialaccount/messages/account_not_found.txt"
  121. get_adapter().add_message(request, level, message)
  122. raise ImmediateHttpResponse(
  123. redirect('socialaccount_signup')
  124. )
  125. # TODO: Can the code that uses this just use request.session['socialaccount_sociallogin'].account.provider instead?
  126. request.session['sociallogin_provider'] = (sociallogin
  127. .account.provider)
  128. request.session.modified = True