pipeline.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. # pylint: disable=keyword-arg-before-vararg
  2. import json
  3. from django.contrib.auth import get_user_model
  4. from django.db import IntegrityError
  5. from django.http import JsonResponse
  6. from django.shortcuts import render
  7. from django.urls import reverse
  8. from django.utils.translation import gettext as _
  9. from social_core.pipeline.partial import partial
  10. from unidecode import unidecode
  11. from ..core.exceptions import SocialAuthBanned, SocialAuthFailed
  12. from ..legal.models import Agreement
  13. from ..users.bans import get_request_ip_ban, get_user_ban
  14. from ..users.forms.register import SocialAuthRegisterForm
  15. from ..users.models import Ban
  16. from ..users.registration import (
  17. get_registration_result_json,
  18. save_user_agreements,
  19. send_welcome_email,
  20. )
  21. from ..users.setupnewuser import setup_new_user
  22. from ..users.validators import (
  23. ValidationError,
  24. validate_email,
  25. validate_new_registration,
  26. validate_username,
  27. )
  28. from .providers import providers
  29. User = get_user_model()
  30. def validate_ip_not_banned(strategy, details, backend, user=None, *args, **kwargs):
  31. """
  32. Pipeline step that interrupts pipeline if found user is non-staff and IP banned
  33. """
  34. if not user or user.is_staff:
  35. return None
  36. ban = get_request_ip_ban(strategy.request)
  37. if ban:
  38. hydrated_ban = Ban(
  39. check_type=Ban.IP, user_message=ban["message"], expires_on=ban["expires_on"]
  40. )
  41. raise SocialAuthBanned(backend, hydrated_ban)
  42. def validate_user_not_banned(strategy, details, backend, user=None, *args, **kwargs):
  43. """Pipeline step that interrupts pipeline if found user is non-staff and banned"""
  44. if not user or user.is_staff:
  45. return None
  46. user_ban = get_user_ban(user, strategy.request.cache_versions)
  47. if user_ban:
  48. raise SocialAuthBanned(backend, user_ban)
  49. def perpare_username(username):
  50. return "".join(filter(str.isalnum, unidecode(username)))
  51. def associate_by_email(strategy, details, backend, user=None, *args, **kwargs):
  52. """If user with e-mail from provider exists in database and is active,
  53. this step authenticates them.
  54. """
  55. enable_step = strategy.setting("ASSOCIATE_BY_EMAIL", default=False, backend=backend)
  56. if user or not enable_step:
  57. return None
  58. email = details.get("email")
  59. if not email:
  60. return None
  61. try:
  62. user = User.objects.get_by_email(email)
  63. except User.DoesNotExist:
  64. return None
  65. backend_name = providers.get_name(backend.name)
  66. if not user.is_active:
  67. raise SocialAuthFailed(
  68. backend,
  69. _(
  70. "The e-mail address associated with your %(backend)s account is "
  71. "not available for use on this site."
  72. )
  73. % {"backend": backend_name},
  74. )
  75. if user.requires_activation_by_admin:
  76. raise SocialAuthFailed(
  77. backend,
  78. _(
  79. "Your account has to be activated by site administrator "
  80. "before you will be able to sign in with %(backend)s."
  81. )
  82. % {"backend": backend_name},
  83. )
  84. return {"user": user, "is_new": False}
  85. def get_username(strategy, details, backend, user=None, *args, **kwargs):
  86. """Resolve valid username for use in new account"""
  87. if user:
  88. return None
  89. settings = strategy.request.settings
  90. username = perpare_username(details.get("username", ""))
  91. full_name = perpare_username(details.get("full_name", ""))
  92. first_name = perpare_username(details.get("first_name", ""))
  93. last_name = perpare_username(details.get("last_name", ""))
  94. names_to_try = [username, first_name]
  95. if username:
  96. names_to_try.append(username)
  97. if first_name:
  98. names_to_try.append(first_name)
  99. if last_name:
  100. # if first name is taken, try first name + first char of last name
  101. names_to_try.append(first_name + last_name[0])
  102. if full_name:
  103. names_to_try.append(full_name)
  104. username_length_max = settings.username_length_max
  105. for name in names_to_try:
  106. if len(name) > username_length_max:
  107. names_to_try.append(name[:username_length_max])
  108. for name in filter(bool, names_to_try):
  109. try:
  110. validate_username(settings, name)
  111. return {"clean_username": name}
  112. except ValidationError:
  113. pass
  114. def create_user(strategy, details, backend, user=None, *args, **kwargs):
  115. """Aggressively attempt to register and sign in new user"""
  116. if user:
  117. return None
  118. request = strategy.request
  119. settings = request.settings
  120. email = details.get("email")
  121. username = kwargs.get("clean_username")
  122. if not email or not username:
  123. return None
  124. try:
  125. validate_email(email)
  126. validate_new_registration(request, {"email": email, "username": username})
  127. except ValidationError:
  128. return None
  129. activation_kwargs = {}
  130. if settings.account_activation == "admin":
  131. activation_kwargs = {"requires_activation": User.ACTIVATION_ADMIN}
  132. new_user = User.objects.create_user(
  133. username, email, joined_from_ip=request.user_ip, **activation_kwargs
  134. )
  135. setup_new_user(settings, new_user)
  136. send_welcome_email(request, new_user)
  137. return {"user": new_user, "is_new": True}
  138. @partial
  139. def create_user_with_form(strategy, details, backend, user=None, *args, **kwargs):
  140. """
  141. create_user lets user confirm account creation by entering final username or email
  142. """
  143. if user:
  144. return None
  145. request = strategy.request
  146. settings = request.settings
  147. backend_name = providers.get_name(backend.name)
  148. if request.method == "POST":
  149. try:
  150. request_data = json.loads(request.body)
  151. except (TypeError, ValueError):
  152. request_data = request.POST.copy()
  153. form = SocialAuthRegisterForm(
  154. request_data, request=request, agreements=Agreement.objects.get_agreements()
  155. )
  156. if not form.is_valid():
  157. return JsonResponse(form.errors, status=400)
  158. email_verified = form.cleaned_data["email"] == details.get("email")
  159. activation_kwargs = {}
  160. if settings.account_activation == "admin":
  161. activation_kwargs = {"requires_activation": User.ACTIVATION_ADMIN}
  162. elif settings.account_activation == "user" and not email_verified:
  163. activation_kwargs = {"requires_activation": User.ACTIVATION_USER}
  164. try:
  165. new_user = User.objects.create_user(
  166. form.cleaned_data["username"],
  167. form.cleaned_data["email"],
  168. joined_from_ip=request.user_ip,
  169. **activation_kwargs
  170. )
  171. setup_new_user(settings, new_user)
  172. except IntegrityError:
  173. return JsonResponse(
  174. {"__all__": _("Please try resubmitting the form.")}, status=400
  175. )
  176. save_user_agreements(new_user, form)
  177. send_welcome_email(request, new_user)
  178. return {"user": new_user, "is_new": True}
  179. request.frontend_context["SOCIAL_AUTH_FORM"] = {
  180. "backend_name": backend_name,
  181. "step": "register",
  182. "email": details.get("email"),
  183. "username": kwargs.get("clean_username"),
  184. "url": reverse("misago:social-complete", kwargs={"backend": backend.name}),
  185. }
  186. return render(request, "misago/socialauth.html", {"backend_name": backend_name})
  187. @partial
  188. def require_activation(
  189. strategy, details, backend, user=None, is_new=False, *args, **kwargs
  190. ):
  191. if not user:
  192. # Social auth pipeline has entered corrupted state
  193. # Remove partial auth state and redirect user to beginning
  194. partial_token = strategy.session.get("partial_pipeline_token")
  195. if partial_token:
  196. strategy.clean_partial_pipeline(partial_token)
  197. return None
  198. if not user.requires_activation:
  199. return None
  200. request = strategy.request
  201. backend_name = providers.get_name(backend.name)
  202. response_data = get_registration_result_json(user)
  203. response_data.update({"step": "done", "backend_name": backend_name})
  204. if request.method == "POST":
  205. # we are carrying on from requestration request
  206. return JsonResponse(response_data)
  207. request.frontend_context["SOCIAL_AUTH_FORM"] = response_data
  208. request.frontend_context["SOCIAL_AUTH_FORM"].update(
  209. {"url": reverse("misago:social-complete", kwargs={"backend": backend.name})}
  210. )
  211. return render(request, "misago/socialauth.html", {"backend_name": backend_name})