import logging from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated from rest_framework.exceptions import PermissionDenied from rest_framework.parsers import MultiPartParser from django.utils.translation import gettext as _ from django.db import transaction from pingcap_sso.api.base import Endpoint from pingcap_sso.api.exceptions import Conflict from pingcap_sso.forms.profile import ( UserProfileForm, ResetPasswordForm, SetPasswordForm, SetPhoneSendCodeForm, SetPhoneForm, SetEmailSendCodeForm, SetEmailForm ) from pingcap_sso.forms.email import EnterpriseEmailForm from pingcap_sso.forms.company import CompanyVerifyApplicationForm from pingcap_sso.models import SecurityCode from social_django.models import UserSocialAuth from pingcap_sso.utils.safe import mask_phone, mask_email from pingcap_sso.exceptions import RateLimitError logger = logging.getLogger(__name__) class ProfileEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def get(self, request, *args, **kwargs): user = request.user return Response({ 'detail': _('success'), 'data': { 'avatar_url': user.avatar_url, 'username': user.username, 'username_last_modified_at': user.username_last_modified_at, 'bio': user.bio, 'name': user.name, 'gender': user.gender, 'date_of_birth': user.date_of_birth, 'address': user.address, 'company_name': user.company_name, 'company_verified_status': user.company_verified_status, 'job_title': user.job_title_code } }) @transaction.atomic def patch(self, request, *args, **kwargs): user = request.user initial = { 'username': user.username, 'bio': user.bio, 'name': user.name, 'gender': user.gender, 'date_of_birth': user.date_of_birth, 'address': user.address, 'company_name': user.company_name, 'job_title': user.job_title_code } data = initial.copy() data.update(request.data) form = UserProfileForm( request, request.user, data=data, initial=initial ) if not form.is_valid(): return Response({ 'detail': _('params wrong'), 'errors': form.errors }, status=400) else: form.save() return Response({'detail': 'success'}) class _CompanyVerifyEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def convert_args(self, request, *args, **kwargs): user = request.user if user.company_verified_status != user.COMPANY_UNVERIFIED: raise Conflict(_('only unverified user can submit application')) if not (user.company_name and user.job_title_code): raise Conflict(_('please fill in company name and job title')) return (args, kwargs) class CompanyVerifySendCodeEndpoint(_CompanyVerifyEndpoint): def post(self, request, *args, **kwargs): form = EnterpriseEmailForm(data=request.data) if not form.is_valid(): return Response({ 'detail': _('params wrong'), 'errors': form.errors }, status=400) try: SecurityCode.send_code( form.cleaned_data['email'], channel=SecurityCode.DELIVER_BY_EMAIL) except RateLimitError: return Response({ 'detail': _('rate limit') }, status=429) else: return Response({'detail': _('success')}) class CompanyVerifySubmitApplicationEndpoint(_CompanyVerifyEndpoint): parser_classes = (MultiPartParser, ) @transaction.atomic def post(self, request, *args, **kwargs): form = CompanyVerifyApplicationForm( request, request.user, data=request.data, files=request.FILES) if not form.is_valid(): return Response({ 'detail': _('params error'), 'errors': form.errors }, status=400) form.save() return Response({ 'detail': _('success') }) class AccountSettingEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def get(self, request, *args, **kwargs): user = request.user github_account = None wechat_account = None try: github_usa = UserSocialAuth.objects.get(user=user, provider='github') github_account = { 'id': github_usa.uid, 'login': github_usa.extra_data.get('login') } except UserSocialAuth.DoesNotExist: pass try: wechat_usa = UserSocialAuth.objects.get(user=user, provider='wechat') wechat_account = { 'id': wechat_usa.uid, } except UserSocialAuth.DoesNotExist: pass return Response({ 'detail': _('success'), 'data': { 'phone': mask_phone(user.phone), 'email': mask_email(user.email or user.signup_email), 'email_verified': user.email_verified, 'has_password': user.has_password, 'associated_accounts': { 'github': github_account, 'wechat': wechat_account }, } }) class ResetPasswordEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def post(self, request, *args, **kwargs): form = ResetPasswordForm(request.user, data=request.data) if not form.is_valid(): return Response({ 'detail': _('params wrong'), 'errors': form.errors }, status=400) else: form.save() return Response({'detail': 'success'}) class SetPasswordEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def post(self, request, *args, **kwargs): if request.user.has_password: raise PermissionDenied form = SetPasswordForm(request.user, data=request.data) if not form.is_valid(): return Response({ 'detail': _('params wrong'), 'errors': form.errors }, status=400) else: form.save() return Response({'detail': 'success'}) class SetPhoneSendCodeEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def post(self, request, *args, **kwargs): form = SetPhoneSendCodeForm(request.user, data=request.data) if not form.is_valid(): return Response({ 'detail': _('params wrong'), 'errors': form.errors }, status=400) else: try: form.save() return Response({'detail': 'success'}) except RateLimitError: return Response({ 'detail': _('rate limit') }, status=429) class SetPhoneEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def post(self, request, *args, **kwargs): form = SetPhoneForm(request.user, data=request.data) if not form.is_valid(): return Response({ 'detail': _('params wrong'), 'errors': form.errors }, status=400) else: form.save() return Response({'detail': 'success'}) class SetEmailSendCodeEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def post(self, request, *args, **kwargs): form = SetEmailSendCodeForm(request.user, data=request.data) if not form.is_valid(): return Response({ 'detail': _('params wrong'), 'errors': form.errors }, status=400) else: try: form.save() return Response({'detail': 'success'}) except RateLimitError: return Response({ 'detail': _('rate limit') }, status=429) class SetEmailEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def post(self, request, *args, **kwargs): form = SetEmailForm(request.user, data=request.data) if not form.is_valid(): return Response({ 'detail': _('params wrong'), 'errors': form.errors }, status=400) else: form.save() return Response({'detail': 'success'}) class SocialDisconnectEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) PROVIDERS = ('github', 'wechat') def post(self, request, *args, **kwargs): provider = kwargs.get('provider') if provider not in self.PROVIDERS: raise PermissionDenied user = request.user if not user.phone: raise Conflict(_('you need to set a phone number before unbinding social account')) UserSocialAuth.objects.filter(user=user, provider=provider).delete() return Response({'detail': 'success'})