import logging from urllib.parse import urlencode, quote_plus from rest_framework.response import Response from rest_framework.permissions import AllowAny from django.utils.translation import gettext as _ from django.contrib.auth import login from django.urls import reverse from django.conf import settings from pingcap_sso.api.base import Endpoint from pingcap_sso.forms.login import ( PhoneCheckForm, PhoneSendCodeForm, PhoneLoginForm, PasswordLoginForm, UpdatePhoneSendCodeForm, ) from pingcap_sso.models import SecurityCode from pingcap_sso.exceptions import RateLimitError, CaptchaRequiredError from pingcap_sso.lib.sso import sync_cookie_url from pingcap_sso.lib.captcha import generate_new_captcha logger = logging.getLogger(__name__) class PhoneCheckEndpoint(Endpoint): permission_classes = (AllowAny, ) def post(self, request, *args, **kwargs): form = PhoneCheckForm(data=request.data) if not form.is_valid(): return Response({ 'detail': _('params error'), 'errors': form.errors }, status=400) return Response({ 'detail': _('success') }) class PhoneSendCodeEndpoint(Endpoint): permission_classes = (AllowAny, ) def post(self, request, *args, **kwargs): form = PhoneSendCodeForm(data=request.data) if not form.is_valid(): return Response({ 'detail': _('params error'), 'errors': form.errors }, status=400) try: SecurityCode.send_code( form.cleaned_data['phone'], channel=SecurityCode.DELIVER_BY_PHONE) except RateLimitError: return Response({ 'detail': _('rate limit') }, status=429) else: return Response({'detail': _('success')}) class LoginEndpointMixin: form_class = None def post(self, request, *args, **kwargs): form = self.form_class(request, data=request.data) if not form.is_valid(): return Response({ 'detail': _('params error'), 'errors': form.errors }, status=400) login(request, form.get_user()) return Response({ 'detail': _('success'), 'data': { 'redirect_to': sync_cookie_url(form.cleaned_data['redirect_to']), 'sso_callbacks': [ '{}?sys={}'.format(reverse('set-cookie'), sys) for sys in settings.SSO_SET_COOKIES.keys() ] } }) class PhoneLoginEndpoint(LoginEndpointMixin, Endpoint): form_class = PhoneLoginForm permission_classes = (AllowAny, ) class PasswordLoginEndpoint(LoginEndpointMixin, Endpoint): form_class = PasswordLoginForm permission_classes = (AllowAny, ) class UpdatePhoneSendCodeEndpoint(Endpoint): permission_classes = (AllowAny, ) def post(self, request, *args, **kwargs): form = UpdatePhoneSendCodeForm(request, data=request.data) try: if not form.is_valid(): return Response({ 'detail': _('params error'), 'errors': form.errors }, status=400) except CaptchaRequiredError: return Response({ 'detail': _('captcha required'), 'data': generate_new_captcha() }, status=428) try: form.save() except RateLimitError: return Response({ 'detail': _('rate limit') }, status=429) else: return Response({'detail': _('success')})