diff --git a/account/decorators.py b/account/decorators.py index c11560d..9df97e5 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -1,7 +1,5 @@ import functools -from django.utils.translation import ugettext as _ - from utils.api import JSONResponse from .models import ProblemPermission @@ -22,10 +20,10 @@ class BasePermissionDecorator(object): if self.check_permission(): if self.request.user.is_disabled: - return self.error(_("Your account is disabled")) + return self.error("Your account is disabled") return self.func(*args, **kwargs) else: - return self.error(_("Please login in first")) + return self.error("Please login in first") def check_permission(self): raise NotImplementedError() diff --git a/account/tasks.py b/account/tasks.py index 00248ba..0aacec9 100644 --- a/account/tasks.py +++ b/account/tasks.py @@ -1,8 +1,8 @@ from celery import shared_task -from utils.mail import send_email +from utils.shortcuts import send_email @shared_task -def _send_email(from_name, to_email, to_name, subject, content): +def send_email_async(from_name, to_email, to_name, subject, content): send_email(from_name, to_email, to_name, subject, content) diff --git a/account/tests.py b/account/tests.py index 877b317..d4addd5 100644 --- a/account/tests.py +++ b/account/tests.py @@ -2,7 +2,6 @@ import time from unittest import mock from django.contrib import auth -from django.utils.translation import ugettext as _ from otpauth import OtpAuth from utils.api.tests import APIClient, APITestCase @@ -45,7 +44,7 @@ class UserLoginAPITest(APITestCase): def test_login_with_correct_info(self): response = self.client.post(self.login_url, data={"username": self.username, "password": self.password}) - self.assertDictEqual(response.data, {"error": None, "data": _("Succeeded")}) + self.assertDictEqual(response.data, {"error": None, "data": "Succeeded"}) user = auth.get_user(self.client) self.assertTrue(user.is_authenticated()) @@ -53,7 +52,7 @@ class UserLoginAPITest(APITestCase): def test_login_with_wrong_info(self): response = self.client.post(self.login_url, data={"username": self.username, "password": "invalid_password"}) - self.assertDictEqual(response.data, {"error": "error", "data": _("Invalid username or password")}) + self.assertDictEqual(response.data, {"error": "error", "data": "Invalid username or password"}) user = auth.get_user(self.client) self.assertFalse(user.is_authenticated()) @@ -67,7 +66,7 @@ class UserLoginAPITest(APITestCase): data={"username": self.username, "password": self.password, "tfa_code": code}) - self.assertDictEqual(response.data, {"error": None, "data": _("Succeeded")}) + self.assertDictEqual(response.data, {"error": None, "data": "Succeeded"}) user = auth.get_user(self.client) self.assertTrue(user.is_authenticated()) @@ -78,7 +77,7 @@ class UserLoginAPITest(APITestCase): data={"username": self.username, "password": self.password, "tfa_code": "qqqqqq"}) - self.assertDictEqual(response.data, {"error": "error", "data": _("Invalid two factor verification code")}) + self.assertDictEqual(response.data, {"error": "error", "data": "Invalid two factor verification code"}) user = auth.get_user(self.client) self.assertFalse(user.is_authenticated()) @@ -116,7 +115,7 @@ class UserRegisterAPITest(CaptchaTest): def test_invalid_captcha(self): self.data["captcha"] = "****" response = self.client.post(self.register_url, data=self.data) - self.assertDictEqual(response.data, {"error": "error", "data": _("Invalid captcha")}) + self.assertDictEqual(response.data, {"error": "error", "data": "Invalid captcha"}) self.data.pop("captcha") response = self.client.post(self.register_url, data=self.data) @@ -124,7 +123,7 @@ class UserRegisterAPITest(CaptchaTest): def test_register_with_correct_info(self): response = self.client.post(self.register_url, data=self.data) - self.assertDictEqual(response.data, {"error": None, "data": _("Succeeded")}) + self.assertDictEqual(response.data, {"error": None, "data": "Succeeded"}) def test_username_already_exists(self): self.test_register_with_correct_info() @@ -132,7 +131,7 @@ class UserRegisterAPITest(CaptchaTest): self.data["captcha"] = self._set_captcha(self.client.session) self.data["email"] = "test1@qduoj.com" response = self.client.post(self.register_url, data=self.data) - self.assertDictEqual(response.data, {"error": "error", "data": _("Username already exists")}) + self.assertDictEqual(response.data, {"error": "error", "data": "Username already exists"}) def test_email_already_exists(self): self.test_register_with_correct_info() @@ -140,7 +139,7 @@ class UserRegisterAPITest(CaptchaTest): self.data["captcha"] = self._set_captcha(self.client.session) self.data["username"] = "test_user1" response = self.client.post(self.register_url, data=self.data) - self.assertDictEqual(response.data, {"error": "error", "data": _("Email already exists")}) + self.assertDictEqual(response.data, {"error": "error", "data": "Email already exists"}) class UserChangePasswordAPITest(CaptchaTest): @@ -159,19 +158,19 @@ class UserChangePasswordAPITest(CaptchaTest): def test_login_required(self): response = self.client.post(self.url, data=self.data) - self.assertEqual(response.data, {"error": "permission-denied", "data": _("Please login in first")}) + self.assertEqual(response.data, {"error": "permission-denied", "data": "Please login in first"}) def test_valid_ola_password(self): self.assertTrue(self.client.login(username=self.username, password=self.old_password)) response = self.client.post(self.url, data=self.data) - self.assertEqual(response.data, {"error": None, "data": _("Succeeded")}) + self.assertEqual(response.data, {"error": None, "data": "Succeeded"}) self.assertTrue(self.client.login(username=self.username, password=self.new_password)) def test_invalid_old_password(self): self.assertTrue(self.client.login(username=self.username, password=self.old_password)) self.data["old_password"] = "invalid" response = self.client.post(self.url, data=self.data) - self.assertEqual(response.data, {"error": "error", "data": _("Invalid old password")}) + self.assertEqual(response.data, {"error": "error", "data": "Invalid old password"}) class AdminUserTest(APITestCase): diff --git a/account/views/admin.py b/account/views/admin.py index 49e8093..62c5115 100644 --- a/account/views/admin.py +++ b/account/views/admin.py @@ -1,6 +1,5 @@ from django.core.exceptions import MultipleObjectsReturned from django.db.models import Q -from django.utils.translation import ugettext as _ from utils.api import APIView, validate_serializer from utils.shortcuts import rand_str @@ -21,21 +20,21 @@ class UserAdminAPI(APIView): try: user = User.objects.get(id=data["id"]) except User.DoesNotExist: - return self.error(_("User does not exist")) + return self.error("User does not exist") try: user = User.objects.get(username=data["username"]) if user.id != data["id"]: - return self.error(_("Username already exists")) + return self.error("Username already exists") except User.DoesNotExist: pass try: user = User.objects.get(email=data["email"]) if user.id != data["id"]: - return self.error(_("Email already exists")) + return self.error("Email already exists") # Some old data has duplicate email except MultipleObjectsReturned: - return self.error(_("Email already exists")) + return self.error("Email already exists") except User.DoesNotExist: pass @@ -85,7 +84,7 @@ class UserAdminAPI(APIView): try: user = User.objects.get(id=user_id) except User.DoesNotExist: - return self.error(_("User does not exist")) + return self.error("User does not exist") return self.success(UserSerializer(user).data) user = User.objects.all().order_by("-create_time") diff --git a/account/views/oj.py b/account/views/oj.py index 48a35c3..7db3bbb 100644 --- a/account/views/oj.py +++ b/account/views/oj.py @@ -4,7 +4,6 @@ from django.conf import settings from django.contrib import auth from django.core.exceptions import MultipleObjectsReturned from django.utils.timezone import now -from django.utils.translation import ugettext as _ from otpauth import OtpAuth from conf.models import WebsiteConfig @@ -18,7 +17,7 @@ from ..serializers import (ApplyResetPasswordSerializer, ResetPasswordSerializer, UserChangePasswordSerializer, UserLoginSerializer, UserRegisterSerializer) -from ..tasks import _send_email +from ..tasks import send_email_async class UserLoginAPI(APIView): @@ -33,7 +32,7 @@ class UserLoginAPI(APIView): if user: if not user.two_factor_auth: auth.login(request, user) - return self.success(_("Succeeded")) + return self.success("Succeeded") # `tfa_code` not in post data if user.two_factor_auth and "tfa_code" not in data: @@ -41,11 +40,11 @@ class UserLoginAPI(APIView): if OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]): auth.login(request, user) - return self.success(_("Succeeded")) + return self.success("Succeeded") else: - return self.error(_("Invalid two factor verification code")) + return self.error("Invalid two factor verification code") else: - return self.error(_("Invalid username or password")) + return self.error("Invalid username or password") # todo remove this, only for debug use def get(self, request): @@ -62,24 +61,24 @@ class UserRegisterAPI(APIView): data = request.data captcha = Captcha(request) if not captcha.check(data["captcha"]): - return self.error(_("Invalid captcha")) + return self.error("Invalid captcha") try: User.objects.get(username=data["username"]) - return self.error(_("Username already exists")) + return self.error("Username already exists") except User.DoesNotExist: pass try: User.objects.get(email=data["email"]) - return self.error(_("Email already exists")) + return self.error("Email already exists") # Some old data has duplicate email except MultipleObjectsReturned: - return self.error(_("Email already exists")) + 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")) + return self.success("Succeeded") class UserChangePasswordAPI(APIView): @@ -92,15 +91,15 @@ class UserChangePasswordAPI(APIView): data = request.data captcha = Captcha(request) if not captcha.check(data["captcha"]): - return self.error(_("Invalid captcha")) + return self.error("Invalid captcha") username = request.user.username user = auth.authenticate(username=username, password=data["old_password"]) if user: user.set_password(data["new_password"]) user.save() - return self.success(_("Succeeded")) + return self.success("Succeeded") else: - return self.error(_("Invalid old password")) + return self.error("Invalid old password") class ApplyResetPasswordAPI(APIView): @@ -110,14 +109,14 @@ class ApplyResetPasswordAPI(APIView): captcha = Captcha(request) config = WebsiteConfig.objects.first() if not captcha.check(data["captcha"]): - return self.error(_("Invalid captcha")) + return self.error("Invalid captcha") try: user = User.objects.get(email=data["email"]) except User.DoesNotExist: - return self.error(_("User does not exist")) + return self.error("User does not exist") if user.reset_password_token_expire_time and 0 < ( user.reset_password_token_expire_time - now()).total_seconds() < 20 * 60: - return self.error(_("You can only reset password once per 20 minutes")) + return self.error("You can only reset password once per 20 minutes") user.reset_password_token = rand_str() user.reset_password_token_expire_time = now() + timedelta(minutes=20) @@ -128,12 +127,12 @@ class ApplyResetPasswordAPI(APIView): replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]). \ replace("{{ link }}", settings.WEBSITE_INFO["url"] + "/reset_password/t/" + user.reset_password_token) - _send_email.delay(config.name, - user.email, - user.username, - config.name + " 登录信息找回邮件", - email_template) - return self.success(_("Succeeded")) + send_email_async.delay(config.name, + user.email, + user.username, + config.name + " 登录信息找回邮件", + email_template) + return self.success("Succeeded") class ResetPasswordAPI(APIView): @@ -142,14 +141,14 @@ class ResetPasswordAPI(APIView): data = request.data captcha = Captcha(request) if not captcha.check(data["captcha"]): - return self.error(_("Invalid captcha")) + return self.error("Invalid captcha") try: user = User.objects.get(reset_password_token=data["token"]) except User.DoesNotExist: - return self.error(_("Token dose not exist")) + return self.error("Token dose not exist") if 0 < (user.reset_password_token_expire_time - now()).total_seconds() < 30 * 60: - return self.error(_("Token expired")) + return self.error("Token expired") user.reset_password_token = None user.set_password(data["password"]) user.save() - return self.success(_("Succeeded")) + return self.success("Succeeded") diff --git a/account/views/user.py b/account/views/user.py index 94b8ccb..19eb893 100644 --- a/account/views/user.py +++ b/account/views/user.py @@ -4,7 +4,6 @@ from io import StringIO import qrcode from django.conf import settings from django.http import HttpResponse -from django.utils.translation import ugettext as _ from otpauth import OtpAuth from conf.models import WebsiteConfig @@ -50,19 +49,19 @@ class UserProfileAPI(APIView): user_profile.major = data["major"] # Timezone & language 暂时不加 user_profile.save() - return self.success(_("Succeeded")) + return self.success("Succeeded") class AvatarUploadAPI(APIView): def post(self, request): if "file" not in request.FILES: - return self.error(_("Upload failed")) + return self.error("Upload failed") f = request.FILES["file"] if f.size > 1024 * 1024: - return self.error(_("Picture too large")) + return self.error("Picture too large") if os.path.splitext(f.name)[-1].lower() not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]: - return self.error(_("Unsupported file format")) + return self.error("Unsupported file format") name = "avatar_" + rand_str(5) + os.path.splitext(f.name)[-1] with open(os.path.join(settings.IMAGE_UPLOAD_DIR, name), "wb") as img: @@ -76,7 +75,7 @@ class SSOAPI(APIView): def get(self, request): callback = request.GET.get("callback", None) if not callback: - return self.error(_("Parameter Error")) + return self.error("Parameter Error") token = rand_str() request.user.auth_token = token request.user.save() @@ -89,7 +88,7 @@ class SSOAPI(APIView): try: User.objects.get(open_api_appkey=data["appkey"]) except User.DoesNotExist: - return self.error(_("Invalid appkey")) + return self.error("Invalid appkey") try: user = User.objects.get(auth_token=data["token"]) user.auth_token = None @@ -133,9 +132,9 @@ class TwoFactorAuthAPI(APIView): if OtpAuth(user.tfa_token).valid_totp(code): user.two_factor_auth = True user.save() - return self.success(_("Succeeded")) + return self.success("Succeeded") else: - return self.error(_("Invalid captcha")) + return self.error("Invalid captcha") @login_required @validate_serializer(TwoFactorAuthCodeSerializer) @@ -146,4 +145,4 @@ class TwoFactorAuthAPI(APIView): user.two_factor_auth = False user.save() else: - return self.error(_("Invalid captcha")) + return self.error("Invalid captcha") diff --git a/announcement/views.py b/announcement/views.py index 8cbe78e..f607a7d 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -1,5 +1,3 @@ -from django.utils.translation import ugettext as _ - from account.decorators import super_admin_required from utils.api import APIView, validate_serializer @@ -32,7 +30,7 @@ class AnnouncementAdminAPI(APIView): try: announcement = Announcement.objects.get(id=data["id"]) except Announcement.DoesNotExist: - return self.error(_("Announcement does not exist")) + return self.error("Announcement does not exist") announcement.title = data["title"] announcement.content = data["content"] @@ -52,7 +50,7 @@ class AnnouncementAdminAPI(APIView): announcement = Announcement.objects.get(id=announcement_id) return self.success(AnnouncementSerializer(announcement).data) except Announcement.DoesNotExist: - return self.error(_("Announcement does not exist")) + return self.error("Announcement does not exist") announcement = Announcement.objects.all().order_by("-create_time") if request.GET.get("visible") == "true": announcement = announcement.filter(visible=True) diff --git a/utils/mail.py b/utils/mail.py deleted file mode 100644 index 6efcc33..0000000 --- a/utils/mail.py +++ /dev/null @@ -1,18 +0,0 @@ -from envelopes import Envelope - -from conf.models import SMTPConfig - - -def send_email(from_name, to_email, to_name, subject, content): - smtp = SMTPConfig.objects.first() - if not smtp: - return - envlope = Envelope(from_addr=(smtp.email, from_name), - to_addr=(to_email, to_name), - subject=subject, - html_body=content) - envlope.send(smtp.server, - login=smtp.email, - password=smtp.password, - port=smtp.port, - tls=smtp.tls) diff --git a/utils/shortcuts.py b/utils/shortcuts.py index 1dd8834..9962ade 100644 --- a/utils/shortcuts.py +++ b/utils/shortcuts.py @@ -2,10 +2,33 @@ import logging import random from django.utils.crypto import get_random_string +from envelopes import Envelope + +from conf.models import SMTPConfig logger = logging.getLogger(__name__) +def send_email(from_name, to_email, to_name, subject, content): + smtp = SMTPConfig.objects.first() + if not smtp: + return + envlope = Envelope(from_addr=(smtp.email, from_name), + to_addr=(to_email, to_name), + subject=subject, + html_body=content) + try: + envlope.send(smtp.server, + login=smtp.email, + password=smtp.password, + port=smtp.port, + tls=smtp.tls) + return True + except Exception as e: + logger.exception(e) + return False + + def rand_str(length=32, type="lower_hex"): """ 生成指定长度的随机字符串或者数字, 可以用于密钥等安全场景