整理代码
This commit is contained in:
@@ -15,6 +15,7 @@ class UsernameOrEmailCheckSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(max_length=30, required=False)
|
||||
email = serializers.EmailField(max_length=30, required=False)
|
||||
|
||||
|
||||
class UserRegisterSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(max_length=30)
|
||||
password = serializers.CharField(max_length=30, min_length=6)
|
||||
@@ -46,7 +47,6 @@ class UserProfileSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class UserInfoSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ from django.conf.urls import url
|
||||
|
||||
from ..views.oj import (ApplyResetPasswordAPI, ResetPasswordAPI,
|
||||
UserChangePasswordAPI, UserRegisterAPI,
|
||||
UserLoginAPI, UserLogoutAPI, UsernameOrEmailCheck)
|
||||
UserLoginAPI, UserLogoutAPI, UsernameOrEmailCheck,
|
||||
SSOAPI, AvatarUploadAPI, TwoFactorAuthAPI, UserProfileAPI)
|
||||
|
||||
from utils.captcha.views import CaptchaAPIView
|
||||
|
||||
@@ -14,5 +15,9 @@ urlpatterns = [
|
||||
url(r"^apply_reset_password/?$", ApplyResetPasswordAPI.as_view(), name="apply_reset_password_api"),
|
||||
url(r"^reset_password/?$", ResetPasswordAPI.as_view(), name="apply_reset_password_api"),
|
||||
url(r"^captcha/?$", CaptchaAPIView.as_view(), name="show_captcha"),
|
||||
url(r"^check_username_or_email", UsernameOrEmailCheck.as_view(), name="check_username_or_email")
|
||||
url(r"^check_username_or_email", UsernameOrEmailCheck.as_view(), name="check_username_or_email"),
|
||||
url(r"^profile/?$", UserProfileAPI.as_view(), name="user_profile_api"),
|
||||
url(r"^avatar/upload/?$", AvatarUploadAPI.as_view(), name="avatar_upload_api"),
|
||||
url(r"^sso/?$", SSOAPI.as_view(), name="sso_api"),
|
||||
url(r"^two_factor_auth/?$", TwoFactorAuthAPI.as_view(), name="two_factor_auth_api")
|
||||
]
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from ..views.user import (SSOAPI, AvatarUploadAPI, TwoFactorAuthAPI,
|
||||
UserProfileAPI)
|
||||
|
||||
urlpatterns = [
|
||||
# url(r"^username/?$", UserNameAPI.as_view(), name="user_name_api"),
|
||||
url(r"^profile/?$", UserProfileAPI.as_view(), name="user_profile_api"),
|
||||
url(r"^avatar/upload/?$", AvatarUploadAPI.as_view(), name="avatar_upload_api"),
|
||||
url(r"^sso/?$", SSOAPI.as_view(), name="sso_api"),
|
||||
url(r"^two_factor_auth/?$", TwoFactorAuthAPI.as_view(), name="two_factor_auth_api")
|
||||
]
|
||||
@@ -1,13 +1,18 @@
|
||||
import os
|
||||
import qrcode
|
||||
from io import BytesIO
|
||||
from datetime import timedelta
|
||||
from otpauth import OtpAuth
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import auth
|
||||
from django.core.exceptions import MultipleObjectsReturned
|
||||
from django.utils.timezone import now
|
||||
from otpauth import OtpAuth
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.utils.decorators import method_decorator
|
||||
|
||||
from conf.models import WebsiteConfig
|
||||
from utils.api import APIView, validate_serializer
|
||||
from utils.api import APIView, validate_serializer, CSRFExemptAPIView
|
||||
from utils.captcha import Captcha
|
||||
from utils.shortcuts import rand_str
|
||||
|
||||
@@ -17,9 +22,153 @@ from ..serializers import (ApplyResetPasswordSerializer,
|
||||
ResetPasswordSerializer,
|
||||
UserChangePasswordSerializer, UserLoginSerializer,
|
||||
UserRegisterSerializer, UsernameOrEmailCheckSerializer)
|
||||
from ..serializers import (SSOSerializer, TwoFactorAuthCodeSerializer,
|
||||
UserProfileSerializer,
|
||||
EditUserProfileSerializer, AvatarUploadForm)
|
||||
from ..tasks import send_email_async
|
||||
|
||||
|
||||
class UserProfileAPI(APIView):
|
||||
"""
|
||||
判断是否登录, 若登录返回用户信息
|
||||
"""
|
||||
@method_decorator(ensure_csrf_cookie)
|
||||
def get(self, request, **kwargs):
|
||||
user = request.user
|
||||
if not user.is_authenticated():
|
||||
return self.success(0)
|
||||
|
||||
username = request.GET.get("username")
|
||||
try:
|
||||
if username:
|
||||
user = User.objects.get(username=username, is_disabled=False)
|
||||
else:
|
||||
user = request.user
|
||||
except User.DoesNotExist:
|
||||
return self.error("User does not exist")
|
||||
profile = UserProfile.objects.get(user=user)
|
||||
return self.success(UserProfileSerializer(profile).data)
|
||||
|
||||
@validate_serializer(EditUserProfileSerializer)
|
||||
@login_required
|
||||
def put(self, request):
|
||||
data = request.data
|
||||
user_profile = request.user.userprofile
|
||||
print(data)
|
||||
if data.get("avatar"):
|
||||
user_profile.avatar = data["avatar"]
|
||||
else:
|
||||
user_profile.mood = data["mood"]
|
||||
user_profile.blog = data["blog"]
|
||||
user_profile.school = data["school"]
|
||||
user_profile.student_id = data["student_id"]
|
||||
user_profile.phone_number = data["phone_number"]
|
||||
user_profile.major = data["major"]
|
||||
# Timezone & language 暂时不加
|
||||
user_profile.save()
|
||||
return self.success("Succeeded")
|
||||
|
||||
|
||||
class AvatarUploadAPI(CSRFExemptAPIView):
|
||||
request_parsers = ()
|
||||
|
||||
def post(self, request):
|
||||
form = AvatarUploadForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
avatar = form.cleaned_data["file"]
|
||||
else:
|
||||
return self.error("Upload failed")
|
||||
if avatar.size > 1024 * 1024:
|
||||
return self.error("Picture too large")
|
||||
if os.path.splitext(avatar.name)[-1].lower() not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]:
|
||||
return self.error("Unsupported file format")
|
||||
|
||||
name = "avatar_" + rand_str(5) + os.path.splitext(avatar.name)[-1]
|
||||
with open(os.path.join(settings.IMAGE_UPLOAD_DIR, name), "wb") as img:
|
||||
for chunk in avatar:
|
||||
img.write(chunk)
|
||||
print(os.path.join(settings.IMAGE_UPLOAD_DIR, name))
|
||||
return self.success({"path": "/static/upload/" + name})
|
||||
|
||||
|
||||
class SSOAPI(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
callback = request.GET.get("callback", None)
|
||||
if not callback:
|
||||
return self.error("Parameter Error")
|
||||
token = rand_str()
|
||||
request.user.auth_token = token
|
||||
request.user.save()
|
||||
return self.success({"redirect_url": callback + "?token=" + token,
|
||||
"callback": callback})
|
||||
|
||||
@validate_serializer(SSOSerializer)
|
||||
def post(self, request):
|
||||
data = request.data
|
||||
try:
|
||||
User.objects.get(open_api_appkey=data["appkey"])
|
||||
except User.DoesNotExist:
|
||||
return self.error("Invalid appkey")
|
||||
try:
|
||||
user = User.objects.get(auth_token=data["token"])
|
||||
user.auth_token = None
|
||||
user.save()
|
||||
return self.success({"username": user.username,
|
||||
"id": user.id,
|
||||
"admin_type": user.admin_type,
|
||||
"avatar": user.userprofile.avatar})
|
||||
except User.DoesNotExist:
|
||||
return self.error("User does not exist")
|
||||
|
||||
|
||||
class TwoFactorAuthAPI(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
"""
|
||||
Get QR code
|
||||
"""
|
||||
user = request.user
|
||||
if user.two_factor_auth:
|
||||
return self.error("Already open 2FA")
|
||||
token = rand_str()
|
||||
user.tfa_token = token
|
||||
user.save()
|
||||
|
||||
config = WebsiteConfig.objects.first()
|
||||
image = qrcode.make(OtpAuth(token).to_uri("totp", config.base_url, config.name))
|
||||
buf = BytesIO()
|
||||
image.save(buf, "gif")
|
||||
|
||||
return HttpResponse(buf.getvalue(), "image/gif")
|
||||
|
||||
@login_required
|
||||
@validate_serializer(TwoFactorAuthCodeSerializer)
|
||||
def post(self, request):
|
||||
"""
|
||||
Open 2FA
|
||||
"""
|
||||
code = request.data["code"]
|
||||
user = request.user
|
||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||
user.two_factor_auth = True
|
||||
user.save()
|
||||
return self.success("Succeeded")
|
||||
else:
|
||||
return self.error("Invalid captcha")
|
||||
|
||||
@login_required
|
||||
@validate_serializer(TwoFactorAuthCodeSerializer)
|
||||
def put(self, request):
|
||||
code = request.data["code"]
|
||||
user = request.user
|
||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||
user.two_factor_auth = False
|
||||
user.save()
|
||||
else:
|
||||
return self.error("Invalid captcha")
|
||||
|
||||
|
||||
class UserLoginAPI(APIView):
|
||||
@validate_serializer(UserLoginSerializer)
|
||||
def post(self, request):
|
||||
@@ -89,23 +238,16 @@ class UserRegisterAPI(APIView):
|
||||
captcha = Captcha(request)
|
||||
if not captcha.validate(data["captcha"]):
|
||||
return self.error("Invalid captcha")
|
||||
try:
|
||||
User.objects.get(username=data["username"])
|
||||
if User.objects.filter(username=data["username"]).exists():
|
||||
return self.error("Username already exists")
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
try:
|
||||
User.objects.get(email=data["email"])
|
||||
if User.objects.filter(email=data["email"]).exists():
|
||||
return self.error("Email already exists")
|
||||
# Some old data has duplicate email
|
||||
except MultipleObjectsReturned:
|
||||
return self.error("Email already exists")
|
||||
except User.DoesNotExist:
|
||||
user = User.objects.create(username=data["username"], email=data["email"])
|
||||
user.set_password(data["password"])
|
||||
user.save()
|
||||
UserProfile.objects.create(user=user)
|
||||
return self.success("Succeeded")
|
||||
|
||||
user = User.objects.create(username=data["username"], email=data["email"])
|
||||
user.set_password(data["password"])
|
||||
user.save()
|
||||
UserProfile.objects.create(user=user, time_zone=settings.USER_DEFAULT_TZ)
|
||||
return self.success("Succeeded")
|
||||
|
||||
|
||||
class UserChangePasswordAPI(APIView):
|
||||
@@ -117,7 +259,7 @@ class UserChangePasswordAPI(APIView):
|
||||
"""
|
||||
data = request.data
|
||||
captcha = Captcha(request)
|
||||
if not captcha.check(data["captcha"]):
|
||||
if not captcha.validate(data["captcha"]):
|
||||
return self.error("Invalid captcha")
|
||||
username = request.user.username
|
||||
user = auth.authenticate(username=username, password=data["old_password"])
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
import os
|
||||
from io import BytesIO
|
||||
|
||||
import qrcode
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.utils.decorators import method_decorator
|
||||
from otpauth import OtpAuth
|
||||
|
||||
from conf.models import WebsiteConfig
|
||||
from utils.api import APIView, validate_serializer, CSRFExemptAPIView
|
||||
from utils.shortcuts import rand_str
|
||||
|
||||
from ..decorators import login_required
|
||||
from ..models import User, UserProfile
|
||||
from ..serializers import (SSOSerializer, TwoFactorAuthCodeSerializer,
|
||||
UserProfileSerializer,
|
||||
EditUserProfileSerializer, AvatarUploadForm)
|
||||
|
||||
|
||||
class UserNameAPI(APIView):
|
||||
@method_decorator(ensure_csrf_cookie)
|
||||
def get(self, request):
|
||||
"""
|
||||
Return Username to valid login status
|
||||
"""
|
||||
try:
|
||||
user = User.objects.get(id=request.user.id)
|
||||
except User.DoesNotExist:
|
||||
return self.success({
|
||||
"username": "User does not exist",
|
||||
"isLogin": False
|
||||
})
|
||||
return self.success({
|
||||
"username": user.username,
|
||||
"isLogin": True
|
||||
})
|
||||
|
||||
|
||||
class UserProfileAPI(APIView):
|
||||
"""
|
||||
判断是否登录, 若登录返回用户信息
|
||||
"""
|
||||
@method_decorator(ensure_csrf_cookie)
|
||||
def get(self, request, **kwargs):
|
||||
user = request.user
|
||||
if not user.is_authenticated():
|
||||
return self.success(0)
|
||||
|
||||
username = request.GET.get("username")
|
||||
try:
|
||||
if username:
|
||||
user = User.objects.get(username=username, is_disabled=False)
|
||||
else:
|
||||
user = request.user
|
||||
except User.DoesNotExist:
|
||||
return self.error("User does not exist")
|
||||
profile = UserProfile.objects.get(user=user)
|
||||
return self.success(UserProfileSerializer(profile).data)
|
||||
|
||||
@validate_serializer(EditUserProfileSerializer)
|
||||
@login_required
|
||||
def put(self, request):
|
||||
data = request.data
|
||||
user_profile = request.user.userprofile
|
||||
print(data)
|
||||
if data.get("avatar"):
|
||||
user_profile.avatar = data["avatar"]
|
||||
else:
|
||||
user_profile.mood = data["mood"]
|
||||
user_profile.blog = data["blog"]
|
||||
user_profile.school = data["school"]
|
||||
user_profile.student_id = data["student_id"]
|
||||
user_profile.phone_number = data["phone_number"]
|
||||
user_profile.major = data["major"]
|
||||
# Timezone & language 暂时不加
|
||||
user_profile.save()
|
||||
return self.success("Succeeded")
|
||||
|
||||
|
||||
class AvatarUploadAPI(CSRFExemptAPIView):
|
||||
request_parsers = ()
|
||||
|
||||
def post(self, request):
|
||||
form = AvatarUploadForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
avatar = form.cleaned_data["file"]
|
||||
else:
|
||||
return self.error("Upload failed")
|
||||
if avatar.size > 1024 * 1024:
|
||||
return self.error("Picture too large")
|
||||
if os.path.splitext(avatar.name)[-1].lower() not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]:
|
||||
return self.error("Unsupported file format")
|
||||
|
||||
name = "avatar_" + rand_str(5) + os.path.splitext(avatar.name)[-1]
|
||||
with open(os.path.join(settings.IMAGE_UPLOAD_DIR, name), "wb") as img:
|
||||
for chunk in avatar:
|
||||
img.write(chunk)
|
||||
print(os.path.join(settings.IMAGE_UPLOAD_DIR, name))
|
||||
return self.success({"path": "/static/upload/" + name})
|
||||
|
||||
|
||||
class SSOAPI(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
callback = request.GET.get("callback", None)
|
||||
if not callback:
|
||||
return self.error("Parameter Error")
|
||||
token = rand_str()
|
||||
request.user.auth_token = token
|
||||
request.user.save()
|
||||
return self.success({"redirect_url": callback + "?token=" + token,
|
||||
"callback": callback})
|
||||
|
||||
@validate_serializer(SSOSerializer)
|
||||
def post(self, request):
|
||||
data = request.data
|
||||
try:
|
||||
User.objects.get(open_api_appkey=data["appkey"])
|
||||
except User.DoesNotExist:
|
||||
return self.error("Invalid appkey")
|
||||
try:
|
||||
user = User.objects.get(auth_token=data["token"])
|
||||
user.auth_token = None
|
||||
user.save()
|
||||
return self.success({"username": user.username,
|
||||
"id": user.id,
|
||||
"admin_type": user.admin_type,
|
||||
"avatar": user.userprofile.avatar})
|
||||
except User.DoesNotExist:
|
||||
return self.error("User does not exist")
|
||||
|
||||
|
||||
class TwoFactorAuthAPI(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
"""
|
||||
Get QR code
|
||||
"""
|
||||
user = request.user
|
||||
if user.two_factor_auth:
|
||||
return self.error("Already open 2FA")
|
||||
token = rand_str()
|
||||
user.tfa_token = token
|
||||
user.save()
|
||||
|
||||
config = WebsiteConfig.objects.first()
|
||||
image = qrcode.make(OtpAuth(token).to_uri("totp", config.base_url, config.name))
|
||||
buf = BytesIO()
|
||||
image.save(buf, "gif")
|
||||
|
||||
return HttpResponse(buf.getvalue(), "image/gif")
|
||||
|
||||
@login_required
|
||||
@validate_serializer(TwoFactorAuthCodeSerializer)
|
||||
def post(self, request):
|
||||
"""
|
||||
Open 2FA
|
||||
"""
|
||||
code = request.data["code"]
|
||||
user = request.user
|
||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||
user.two_factor_auth = True
|
||||
user.save()
|
||||
return self.success("Succeeded")
|
||||
else:
|
||||
return self.error("Invalid captcha")
|
||||
|
||||
@login_required
|
||||
@validate_serializer(TwoFactorAuthCodeSerializer)
|
||||
def put(self, request):
|
||||
code = request.data["code"]
|
||||
user = request.user
|
||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||
user.two_factor_auth = False
|
||||
user.save()
|
||||
else:
|
||||
return self.error("Invalid captcha")
|
||||
Reference in New Issue
Block a user