from django import forms from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from django.db import transaction from pingcap_sso.models import User, SecurityCode from pingcap_sso.utils.phone import is_virtual_phone from pingcap_sso.utils.requests import get_safe_redirect_uri from pingcap_sso.user_behavior_track.models import TrackEvent from pingcap_sso.lib.sso import parse_signup_channel_from_url from pingcap_sso.lib.points import ( AWARD_USER_BIND_PHONE, award_points_by_type ) from social_django.models import UserSocialAuth from .phone import PhoneField class SSOSessionDetailForm(forms.Form): ssid = forms.CharField(max_length=255, required=True) class SSOSyncTokenForm(forms.Form): sync_token = forms.CharField(max_length=200, required=True) class UpdatePhoneForm(forms.Form): phone = PhoneField(required=True) code = forms.CharField( max_length=6, min_length=6, required=True, strip=True ) def __init__(self, user, *args, **kwargs): self.user = user super(UpdatePhoneForm, self).__init__(*args, **kwargs) def clean_phone(self): phone = self.cleaned_data.get('phone') if phone: if is_virtual_phone(phone): raise ValidationError(_('currently does not support virtual carrier numbers')) if User.objects.filter(phone=phone).exists(): raise ValidationError(_('already used')) return phone def clean(self): if self._errors: return phone = self.cleaned_data.get('phone') sms_code = self.cleaned_data.get('code') if not SecurityCode.check_code( phone, sms_code, SecurityCode.DELIVER_BY_PHONE, scene=SecurityCode.SCENE_UPDATE_PHONE, ): self.add_error('code', _('invalid security code')) return return self.cleaned_data def save(self): self.user.phone = self.cleaned_data.get('phone') self.user.save() award_points_by_type(self.user, AWARD_USER_BIND_PHONE) TrackEvent.objects.create( openid=self.user.openid, event_type='sso.update_account_settings', event_body={ 'action': 'set_phone' }, description='设置了新手机号', source=TrackEvent.SOURCE_SERVER ) class SocialAuthAddPhoneForm(UpdatePhoneForm): def __init__(self, *args, **kwargs): self.phone_already_used = False kwargs['user'] = None super(SocialAuthAddPhoneForm, self).__init__(*args, **kwargs) def clean_phone(self): phone = self.cleaned_data.get('phone') if phone: if is_virtual_phone(phone): raise ValidationError(_('currently does not support virtual carrier numbers')) self.phone_already_used = User.objects.filter(phone=phone).exists() return phone def clean(self): self.cleaned_data = super(SocialAuthAddPhoneForm, self).clean() return self.cleaned_data def save(self): pass @transaction.atomic def create_social_auth_user(self, provider, uid, extra_data, redirect_to): new_user = User( username=User.generate_random_username(), phone=self.cleaned_data.get('phone'), ) new_user.signup_source_url = redirect_to[:500] new_user.signup_channel = parse_signup_channel_from_url(redirect_to) new_user.save() award_points_by_type(new_user, AWARD_USER_BIND_PHONE) UserSocialAuth.objects.create( user=new_user, provider=provider, uid=uid, extra_data=extra_data ) return new_user class SocialAuthConfirmBindingExistsAccountForm(forms.Form): phone = PhoneField(required=True) def __init__(self, provider, uid, extra_data, *args, **kwargs): self.user = None self.provider = provider self.uid = uid self.extra_data = extra_data super(SocialAuthConfirmBindingExistsAccountForm, self).__init__(*args, **kwargs) def clean_phone(self): phone = self.cleaned_data.get('phone') if phone: try: self.user = User.objects.get(phone=phone) except User.DoesNotExist: raise ValidationError(_('user does not exist')) return phone def clean(self): if self._errors: return if UserSocialAuth.objects.filter(user=self.user, provider=self.provider).exists(): self.add_error('phone', _('user already binded with social account')) return self.cleaned_data def bind_social_auth(self): UserSocialAuth.objects.create( user=self.user, provider=self.provider, uid=self.uid, extra_data=self.extra_data ) return self.user