serializers.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import hashlib
  2. import hmac
  3. from allauth.account import app_settings
  4. from allauth.account.adapter import get_adapter
  5. from allauth.account.forms import default_token_generator
  6. from allauth.account.models import EmailAddress
  7. from allauth.account.utils import filter_users_by_email
  8. from allauth.socialaccount.models import SocialApp
  9. from allauth.socialaccount.providers.openid_connect.views import (
  10. OpenIDConnectOAuth2Adapter,
  11. )
  12. from dj_rest_auth.registration.serializers import (
  13. RegisterSerializer as BaseRegisterSerializer,
  14. )
  15. from dj_rest_auth.registration.serializers import (
  16. SocialAccountSerializer as BaseSocialAccountSerializer,
  17. )
  18. from dj_rest_auth.serializers import PasswordResetSerializer
  19. from django.conf import settings
  20. from django.utils.translation import gettext_lazy as _
  21. from rest_framework import serializers
  22. from glitchtip.constants import SOCIAL_ADAPTER_MAP
  23. from .forms import PasswordSetAndResetForm
  24. from .models import User
  25. class SocialAccountSerializer(BaseSocialAccountSerializer):
  26. email = serializers.SerializerMethodField()
  27. username = serializers.SerializerMethodField()
  28. class Meta(BaseSocialAccountSerializer.Meta):
  29. fields = (
  30. "id",
  31. "provider",
  32. "uid",
  33. "last_login",
  34. "date_joined",
  35. "email",
  36. "username",
  37. )
  38. def get_email(self, obj):
  39. if obj.extra_data:
  40. if "email" in obj.extra_data:
  41. return obj.extra_data.get("email")
  42. return obj.extra_data.get("userPrincipalName") # MS oauth uses this
  43. def get_username(self, obj):
  44. if obj.extra_data:
  45. return obj.extra_data.get("username")
  46. class SocialAppSerializer(serializers.ModelSerializer):
  47. authorize_url = serializers.SerializerMethodField()
  48. scopes = serializers.SerializerMethodField()
  49. provider = serializers.SerializerMethodField()
  50. class Meta:
  51. model = SocialApp
  52. fields = ("provider", "name", "client_id", "authorize_url", "scopes")
  53. def get_authorize_url(self, obj):
  54. request = self.context.get("request")
  55. adapter_cls = SOCIAL_ADAPTER_MAP.get(obj.provider)
  56. if adapter_cls == OpenIDConnectOAuth2Adapter:
  57. adapter = adapter_cls(request, obj.provider_id)
  58. else:
  59. adapter = adapter_cls(request)
  60. if adapter:
  61. return adapter.authorize_url
  62. def get_scopes(self, obj):
  63. request = self.context.get("request")
  64. if request:
  65. provider = obj.get_provider(request)
  66. return provider.get_scope(request)
  67. def get_provider(self, obj):
  68. return obj.provider_id or obj.provider
  69. class ConfirmEmailAddressSerializer(serializers.Serializer):
  70. email = serializers.EmailField()
  71. class EmailAddressSerializer(serializers.ModelSerializer):
  72. isPrimary = serializers.BooleanField(source="primary", read_only=True)
  73. email = serializers.EmailField() # Remove default unique validation
  74. isVerified = serializers.BooleanField(source="verified", read_only=True)
  75. class Meta:
  76. model = EmailAddress
  77. fields = ("isPrimary", "email", "isVerified")
  78. def clean_email(self):
  79. """Validate email as done in allauth.account.forms.AddEmailForm"""
  80. value = self.cleaned_data["email"]
  81. value = get_adapter().clean_email(value)
  82. errors = {
  83. "this_account": _(
  84. "This e-mail address is already associated" " with this account."
  85. ),
  86. "different_account": _(
  87. "This e-mail address is already associated" " with another account."
  88. ),
  89. }
  90. users = filter_users_by_email(value)
  91. on_this_account = [u for u in users if u.pk == self.user.pk]
  92. on_diff_account = [u for u in users if u.pk != self.user.pk]
  93. if on_this_account:
  94. raise serializers.ValidationError(errors["this_account"])
  95. if on_diff_account and app_settings.UNIQUE_EMAIL:
  96. raise serializers.ValidationError(errors["different_account"])
  97. return value
  98. def validate(self, attrs):
  99. if self.context["request"].method == "POST":
  100. # Run extra validation on create
  101. self.user = self.context["request"].user
  102. self.cleaned_data = attrs
  103. attrs["email"] = self.clean_email()
  104. return attrs
  105. def create(self, validated_data):
  106. return EmailAddress.objects.add_email(
  107. self.context["request"], self.user, validated_data["email"], confirm=True
  108. )
  109. def update(self, instance, validated_data):
  110. instance.primary = True
  111. instance.save()
  112. return instance
  113. class UserSerializer(serializers.ModelSerializer):
  114. username = serializers.CharField(source="email", read_only=True)
  115. lastLogin = serializers.DateTimeField(source="last_login", read_only=True)
  116. isSuperuser = serializers.BooleanField(source="is_superuser")
  117. emails = EmailAddressSerializer(many=True, default=[])
  118. identities = SocialAccountSerializer(
  119. source="socialaccount_set", many=True, read_only=True
  120. )
  121. id = serializers.CharField()
  122. isActive = serializers.BooleanField(source="is_active")
  123. dateJoined = serializers.DateTimeField(source="created", read_only=True)
  124. hasPasswordAuth = serializers.BooleanField(
  125. source="has_usable_password", read_only=True
  126. )
  127. class Meta:
  128. model = User
  129. fields = (
  130. "username",
  131. "lastLogin",
  132. "isSuperuser",
  133. "emails",
  134. "identities",
  135. "id",
  136. "isActive",
  137. "name",
  138. "dateJoined",
  139. "hasPasswordAuth",
  140. "email",
  141. "options",
  142. )
  143. class CurrentUserSerializer(UserSerializer):
  144. chatwootIdentifierHash = serializers.SerializerMethodField()
  145. class Meta(UserSerializer.Meta):
  146. fields = UserSerializer.Meta.fields + ("chatwootIdentifierHash",)
  147. def get_chatwootIdentifierHash(self, obj):
  148. if settings.CHATWOOT_WEBSITE_TOKEN and settings.CHATWOOT_IDENTITY_TOKEN:
  149. secret = bytes(settings.CHATWOOT_IDENTITY_TOKEN, "utf-8")
  150. message = bytes(str(obj.id), "utf-8")
  151. hash = hmac.new(secret, message, hashlib.sha256)
  152. return hash.hexdigest()
  153. class RegisterSerializer(BaseRegisterSerializer):
  154. tags = serializers.CharField(
  155. write_only=True,
  156. allow_blank=True,
  157. required=False,
  158. help_text="Additional UTM (analytics) data",
  159. )
  160. def custom_signup(self, request, user):
  161. tags = self.validated_data.get("tags")
  162. if tags:
  163. user.set_register_analytics_tags(tags)
  164. user.save(update_fields=["analytics"])
  165. class UserNotificationsSerializer(serializers.ModelSerializer):
  166. subscribeByDefault = serializers.BooleanField(source="subscribe_by_default")
  167. class Meta:
  168. model = User
  169. fields = ("subscribeByDefault",)
  170. class NoopTokenSerializer(serializers.Serializer):
  171. """dj-rest-auth requires tokens, but we don't use them."""
  172. class PasswordSetResetSerializer(PasswordResetSerializer):
  173. password_reset_form_class = PasswordSetAndResetForm
  174. def save(self):
  175. request = self.context.get("request")
  176. opts = {
  177. "use_https": request.is_secure(),
  178. "from_email": getattr(settings, "DEFAULT_FROM_EMAIL"),
  179. "request": request,
  180. "token_generator": default_token_generator,
  181. "subject_template_name": "registration/password_reset_subject.txt",
  182. "email_template_name": "registration/password_reset_email.txt",
  183. "html_email_template_name": "registration/password_reset_email.html",
  184. }
  185. opts.update(self.get_email_options())
  186. self.reset_form.save(**opts)