修改账户系统以及部分用户权限写法

增加部分测试和注释,完善国际化
This commit is contained in:
virusdefender 2016-06-23 12:19:16 +08:00
parent 61fe5675e0
commit eb02a00859
No known key found for this signature in database
GPG Key ID: 1686FB5677979E61
13 changed files with 555 additions and 204 deletions

View File

@ -1,12 +1,13 @@
# coding=utf-8
from __future__ import unicode_literals
import urllib
import functools
from functools import wraps
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext as _
from utils.shortcuts import error_response, error_page
from .models import SUPER_ADMIN, ADMIN
from .models import AdminType
class BasePermissionDecorator(object):
@ -23,15 +24,15 @@ class BasePermissionDecorator(object):
self.request = args[0]
if self.check_permission():
if self.request.user.is_forbidden is True:
if self.request.user.is_disabled:
if self.request.is_ajax():
return error_response(u"您已被禁用,请联系管理员")
return error_response(_("Your account is disabled"))
else:
return error_page(self.request, u"您已被禁用,请联系管理员")
return error_page(self.request, _("Your account is disabled"))
return self.func(*args, **kwargs)
else:
if self.request.is_ajax():
return error_response(u"请先登录")
return error_response(_("Please login in first"))
else:
return HttpResponseRedirect("/login/?__from=" + urllib.quote(self.request.path))
@ -46,9 +47,9 @@ class login_required(BasePermissionDecorator):
class super_admin_required(BasePermissionDecorator):
def check_permission(self):
return self.request.user.is_authenticated() and self.request.user.admin_type == SUPER_ADMIN
return self.request.user.is_authenticated() and self.request.user.admin_type == AdminType.SUPER_ADMIN
class admin_required(BasePermissionDecorator):
def check_permission(self):
return self.request.user.is_authenticated() and self.request.user.admin_type in [SUPER_ADMIN, ADMIN]
return self.request.user.is_authenticated() and self.request.user.admin_type in [AdminType.SUPER_ADMIN, AdminType.ADMIN]

View File

@ -0,0 +1,56 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-06-13 21:24+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: account/decorators.py:29 account/decorators.py:31
msgid "Your account is disabled"
msgstr "您的账号已被禁用"
#: account/decorators.py:35 account/middleware.py:21
msgid "Please login in first"
msgstr "请先登录"
#: account/tests.py:36 account/tests.py:59 account/tests.py:114
#: account/views.py:51 account/views.py:59 account/views.py:96
#: account/views.py:118
msgid "Succeeded"
msgstr ""
#: account/tests.py:45 account/views.py:63
msgid "Invalid username or password"
msgstr ""
#: account/tests.py:70 account/views.py:61
msgid "Invalid two factor verification code"
msgstr ""
#: account/tests.py:106 account/views.py:78 account/views.py:112
msgid "Invalid captcha"
msgstr ""
#: account/tests.py:122 account/views.py:81
msgid "Username already exists"
msgstr ""
#: account/tests.py:130 account/views.py:86 account/views.py:89
msgid "Email already exists"
msgstr ""
#: account/views.py:120
msgid "Invalid old password"
msgstr ""

View File

@ -2,20 +2,23 @@
import time
import json
import urllib
from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import ugettext as _
from django.contrib import auth
from .models import ADMIN
from .models import AdminType
class SessionSecurityMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated() and request.user.admin_type >= ADMIN:
if request.user.is_authenticated() and request.user.admin_type in [AdminType.ADMIN, AdminType.SUPER_ADMIN]:
if "last_activity" in request.session:
# 24个小时没有活动
# 24 hours passwd since last visit
if time.time() - request.session["last_activity"] >= 24 * 60 * 60:
auth.logout(request)
if request.is_ajax():
return HttpResponse(json.dumps({"code": 1, "data": u"请先登录"}),
return HttpResponse(json.dumps({"code": 1, "data": _("Please login in first")}),
content_type="application/json")
else:
return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.path))

View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-06-13 08:06
from __future__ import unicode_literals
from django.db import migrations, models
import jsonfield.fields
class Migration(migrations.Migration):
dependencies = [
('account', '0021_auto_20160424_1243'),
]
operations = [
migrations.RenameField(
model_name='user',
old_name='is_forbidden',
new_name='is_disabled',
),
migrations.RenameField(
model_name='user',
old_name='openapi_appkey',
new_name='open_api_appkey',
),
migrations.RemoveField(
model_name='userprofile',
name='problems_status',
),
migrations.AddField(
model_name='user',
name='admin_extra_permission',
field=jsonfield.fields.JSONField(default=[]),
),
migrations.AddField(
model_name='user',
name='open_api',
field=models.BooleanField(default=False),
),
]

View File

@ -4,6 +4,31 @@ from django.contrib.auth.models import AbstractBaseUser
from jsonfield import JSONField
# TODO remove these
REGULAR_USER = 0
ADMIN = 1
SUPER_ADMIN = 2
class AdminType(object):
REGULAR_USER = 0
ADMIN = 1
SUPER_ADMIN = 2
class ProblemSolutionStatus(object):
ACCEPTED = 1
PENDING = 2
class AdminExtraPermission(object):
CREATE_PUBLIC_CONTEST = 1
MANAGE_ALL_CONTEST = 2
# 3 and 4 are mutually exclusive
MANAGE_ALL_PROBLEM = 3
# Manage public problem user created
MANAGE_OWN_PROBLEM = 4
class UserManager(models.Manager):
use_in_migrations = True
@ -12,37 +37,28 @@ class UserManager(models.Manager):
return self.get(**{self.model.USERNAME_FIELD: username})
REGULAR_USER = 0
ADMIN = 1
SUPER_ADMIN = 2
class User(AbstractBaseUser):
# 用户名
username = models.CharField(max_length=30, unique=True)
# 真实姓名
real_name = models.CharField(max_length=30, blank=True, null=True)
# 用户邮箱
email = models.EmailField(max_length=254, blank=True, null=True)
# 用户注册时间
create_time = models.DateTimeField(auto_now_add=True, null=True)
# 0代表不是管理员 1是普通管理员 2是超级管理员
# One of UserType
admin_type = models.IntegerField(default=0)
# JSON字典用来表示该用户的问题的解决状态 1为ac2为正在进行
# List of items in AdminExtraPermission
admin_extra_permission = JSONField(default=[])
# Store user problem solution status with json string format
# {"problems": {1: ProblemSolutionStatus.ACCEPTED}, "contest_problems": {20: ProblemSolutionStatus.PENDING)}
problems_status = JSONField(default={})
# 找回密码用的token
reset_password_token = models.CharField(max_length=40, blank=True, null=True)
# token 生成时间
reset_password_token_create_time = models.DateTimeField(blank=True, null=True)
# SSO授权token
# SSO auth token
auth_token = models.CharField(max_length=40, blank=True, null=True)
# 是否开启两步验证
two_factor_auth = models.BooleanField(default=False)
tfa_token = models.CharField(max_length=40, blank=True, null=True)
# open api key
openapi_appkey = models.CharField(max_length=35, blank=True, null=True)
# 是否禁用用户
is_forbidden = models.BooleanField(default=False)
open_api = models.BooleanField(default=False)
open_api_appkey = models.CharField(max_length=35, blank=True, null=True)
is_disabled = models.BooleanField(default=False)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
@ -68,23 +84,21 @@ class UserProfile(models.Model):
codeforces_username = models.CharField(max_length=30, blank=True, null=True)
accepted_problem_number = models.IntegerField(default=0)
submission_number = models.IntegerField(default=0)
# JSON字典用来表示该用户的问题的解决状态 1为ac2为正在进行
problems_status = JSONField(default={})
phone_number = models.CharField(max_length=15, blank=True, null=True)
school = models.CharField(max_length=200, blank=True, null=True)
student_id = models.CharField(max_length=15, blank=True, null=True)
def add_accepted_problem_number(self):
self.accepted_problem_number += 1
self.save(update_fields=["accepted_problem_number"])
self.accepted_problem_number = models.F("accepted_problem_number") + 1
self.save()
def add_submission_number(self):
self.submission_number += 1
self.save(update_fields=["submission_number"])
self.submission_number = models.F("submission_number") + 1
self.save()
def minus_accepted_problem_number(self):
self.accepted_problem_number -= 1
self.save(update_fields=["accepted_problem_number"])
self.accepted_problem_number = models.F("accepted_problem_number") - 1
self.save()
class Meta:
db_table = "user_profile"

View File

@ -1,6 +1,7 @@
# coding=utf-8
from rest_framework import serializers
from utils.serializers import DateTimeTZField, JSONField
from .models import User, UserProfile
@ -21,10 +22,10 @@ class EmailCheckSerializer(serializers.Serializer):
class UserRegisterSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
real_name = serializers.CharField(max_length=30)
school = serializers.CharField(max_length=200, required=False, default=None)
password = serializers.CharField(max_length=30, min_length=6)
email = serializers.EmailField(max_length=254)
captcha = serializers.CharField(max_length=4, min_length=4)
school = serializers.CharField(max_length=200, required=False, default=None)
student_id = serializers.CharField(max_length=15, required=False, default=None)
@ -35,11 +36,14 @@ class UserChangePasswordSerializer(serializers.Serializer):
class UserSerializer(serializers.ModelSerializer):
create_time = DateTimeTZField()
last_login = DateTimeTZField()
admin_extra_permission = serializers.ListField()
class Meta:
model = User
fields = ["id", "username", "real_name", "email", "admin_type",
"create_time", "last_login", "two_factor_auth", "openapi_appkey", "is_forbidden"]
fields = ["id", "username", "real_name", "email", "admin_type", "admin_extra_permission",
"create_time", "last_login", "two_factor_auth", "open_api", "is_disabled"]
class EditUserSerializer(serializers.Serializer):
@ -49,9 +53,11 @@ class EditUserSerializer(serializers.Serializer):
password = serializers.CharField(max_length=30, min_length=6, required=False, default=None)
email = serializers.EmailField(max_length=254)
admin_type = serializers.IntegerField(default=0)
openapi = serializers.BooleanField()
tfa_auth = serializers.BooleanField()
is_forbidden = serializers.BooleanField()
open_api = serializers.BooleanField()
two_factor_auth = serializers.BooleanField()
is_disabled = serializers.BooleanField()
admin_extra_permission = serializers.ListField(required=False, default=[],
child=serializers.IntegerField())
class ApplyResetPasswordSerializer(serializers.Serializer):

View File

@ -0,0 +1,204 @@
# coding=utf-8
from __future__ import unicode_literals
import time
import mock
from django.contrib import auth
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from rest_framework.test import APIClient, APITestCase
from utils.shortcuts import rand_str
from utils.otp_auth import OtpAuth
from .models import User
class PermissionDecoratorTest(APITestCase):
def setUp(self):
self.regular_user = User.objects.create(username="regular_user")
self.admin = User.objects.create(username="admin")
self.super_admin = User.objects.create(username="super_admin")
self.request = mock.MagicMock()
self.request.user.is_authenticated = mock.MagicMock()
def test_login_required(self):
self.request.user.is_authenticated.return_value = False
def test_admin_required(self):
pass
def test_super_admin_required(self):
pass
class UserLoginAPITest(APITestCase):
def setUp(self):
self.username = "testuser"
self.password = "testuserpassword"
self.user = User.objects.create(username=self.username)
self.user.set_password(self.password)
self.user.save()
self.login_url = reverse("user_login_api")
def _set_tfa(self):
self.user.two_factor_auth = True
tfa_token = rand_str(32)
self.user.tfa_token = tfa_token
self.user.save()
return tfa_token
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, {"code": 0, "data": _("Succeeded")})
user = auth.get_user(self.client)
self.assertTrue(user.is_authenticated())
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, {"code": 1, "data": _("Invalid username or password")})
user = auth.get_user(self.client)
self.assertFalse(user.is_authenticated())
def test_tfa_login(self):
token = self._set_tfa()
code = OtpAuth(token).totp()
if len(str(code)) < 6:
code = (6 - len(str(code))) * "0" + str(code)
response = self.client.post(self.login_url,
data={"username": self.username,
"password": self.password,
"tfa_code": code})
self.assertDictEqual(response.data, {"code": 0, "data": _("Succeeded")})
user = auth.get_user(self.client)
self.assertTrue(user.is_authenticated())
def test_tfa_login_wrong_code(self):
self._set_tfa()
response = self.client.post(self.login_url,
data={"username": self.username,
"password": self.password,
"tfa_code": "qqqqqq"})
self.assertDictEqual(response.data, {"code": 1, "data": _("Invalid two factor verification code")})
user = auth.get_user(self.client)
self.assertFalse(user.is_authenticated())
def test_tfa_login_without_code(self):
self._set_tfa()
response = self.client.post(self.login_url,
data={"username": self.username,
"password": self.password})
self.assertDictEqual(response.data, {"code": 0, "data": "tfa_required"})
user = auth.get_user(self.client)
self.assertFalse(user.is_authenticated())
class CaptchaTest(APITestCase):
def _set_captcha(self, session):
captcha = rand_str(4)
session["_django_captcha_key"] = captcha
session["_django_captcha_expires_time"] = int(time.time()) + 30
session.save()
return captcha
class UserRegisterAPITest(CaptchaTest):
def setUp(self):
self.client = APIClient()
self.register_url = reverse("user_register_api")
self.captcha = rand_str(4)
self.data = {"username": "test_user", "password": "testuserpassword",
"real_name": "real_name", "email": "test@qduoj.com",
"captcha": self._set_captcha(self.client.session)}
def test_invalid_captcha(self):
self.data["captcha"] = "****"
response = self.client.post(self.register_url, data=self.data)
self.assertDictEqual(response.data, {"code": 1, "data": _("Invalid captcha")})
self.data.pop("captcha")
response = self.client.post(self.register_url, data=self.data)
self.assertEqual(response.data["code"], 1)
def test_register_with_correct_info(self):
response = self.client.post(self.register_url, data=self.data)
self.assertDictEqual(response.data, {"code": 0, "data": _("Succeeded")})
def test_username_already_exists(self):
self.test_register_with_correct_info()
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, {"code": 1, "data": _("Username already exists")})
def test_email_already_exists(self):
self.test_register_with_correct_info()
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, {"code": 1, "data": _("Email already exists")})
class UserChangePasswordAPITest(CaptchaTest):
def setUp(self):
self.client = APIClient()
self.url = reverse("user_change_password_api")
# Create user at first
self.username = "test_user"
self.old_password = "testuserpassword"
self.new_password = "new_password"
register_data = {"username": self.username, "password": self.old_password,
"real_name": "real_name", "email": "test@qduoj.com",
"captcha": self._set_captcha(self.client.session)}
response = self.client.post(reverse("user_register_api"), data=register_data)
self.assertDictEqual(response.data, {"code": 0, "data": _("Succeeded")})
self.data = {"old_password": self.old_password, "new_password": self.new_password,
"captcha": self._set_captcha(self.client.session)}
def test_login_required(self):
response = self.client.post(self.url, data=self.data, HTTP_X_REQUESTED_WITH="XMLHttpRequest")
self.assertEqual(response.data, {"code": 1, "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, HTTP_X_REQUESTED_WITH="XMLHttpRequest")
self.assertEqual(response.data, {"code": 0, "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, HTTP_X_REQUESTED_WITH="XMLHttpRequest")
self.assertEqual(response.data, {"code": 1, "data": _("Invalid old password")})
class AdminEditUserTest(APITestCase):
def setUp(self):
pass
def test_edit_user_successfully(self):
pass
def test_change_user_admin_type(self):
pass
def test_change_user_permission(self):
pass
def test_change_user_password(self):
pass

View File

@ -3,6 +3,7 @@ import os
import codecs
import qrcode
import StringIO
from django import http
from django.contrib import auth
from django.shortcuts import render
@ -12,62 +13,209 @@ from django.conf import settings
from django.http import HttpResponse
from django.core.exceptions import MultipleObjectsReturned
from django.utils.timezone import now
from django.utils.translation import ugettext as _
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, error_page, paginate, rand_str)
from utils.captcha import Captcha
from utils.otp_auth import OtpAuth
from .tasks import _send_email
from .decorators import login_required
from .models import User, UserProfile
from .models import User, UserProfile, AdminExtraPermission, AdminType
from .serializers import (UserLoginSerializer, UserRegisterSerializer,
UserChangePasswordSerializer,
UserSerializer, EditUserSerializer,
ApplyResetPasswordSerializer, ResetPasswordSerializer,
SSOSerializer, EditUserProfileSerializer,
UserProfileSerializer, TwoFactorAuthCodeSerializer)
TwoFactorAuthCodeSerializer)
from .decorators import super_admin_required
class UserLoginAPIView(APIView):
def post(self, request):
"""
用户登录json api接口
---
request_serializer: UserLoginSerializer
User login api
"""
serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
user = auth.authenticate(username=data["username"], password=data["password"])
# 用户名或密码错误的话 返回None
# None is returned if username or password is wrong
if user:
if not user.two_factor_auth:
auth.login(request, user)
return success_response(u"登录成功")
return success_response(_("Succeeded"))
# 没有输入两步验证的验证码
# `tfa_code` not in post data
if user.two_factor_auth and "tfa_code" not in data:
return success_response("tfa_required")
if OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]):
auth.login(request, user)
return success_response(u"登录成功")
return success_response(_("Succeeded"))
else:
return error_response(u"验证码错误")
return error_response(_("Invalid two factor verification code"))
else:
return error_response(u"用户名或密码错误")
return error_response(_("Invalid username or password"))
else:
return serializer_invalid_response(serializer)
#@login_required
class UserRegisterAPIView(APIView):
def post(self, request):
"""
User register api
"""
serializer = UserRegisterSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(_("Invalid captcha"))
try:
User.objects.get(username=data["username"])
return error_response(_("Username already exists"))
except User.DoesNotExist:
pass
try:
User.objects.get(email=data["email"])
return error_response(_("Email already exists"))
# Some old data has duplicate email
except MultipleObjectsReturned:
return error_response(_("Email already exists"))
except User.DoesNotExist:
user = User.objects.create(username=data["username"], real_name=data["real_name"],
email=data["email"])
user.set_password(data["password"])
user.save()
UserProfile.objects.create(user=user, school=data["school"], student_id=data["student_id"])
return success_response(_("Succeeded"))
else:
return serializer_invalid_response(serializer)
class UserChangePasswordAPIView(APIView):
@login_required
def post(self, request):
"""
User change password api
"""
serializer = UserChangePasswordSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(_("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 success_response(_("Succeeded"))
else:
return error_response(_("Invalid old password"))
else:
return serializer_invalid_response(serializer)
class UserAdminAPIView(APIView):
@super_admin_required
def put(self, request):
"""
Edit user api
"""
serializer = EditUserSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
user = User.objects.get(id=data["id"])
except User.DoesNotExist:
return error_response(_("User does not exist"))
try:
user = User.objects.get(username=data["username"])
if user.id != data["id"]:
return error_response(_("Username already exists"))
except User.DoesNotExist:
pass
try:
user = User.objects.get(email=data["email"])
if user.id != data["id"]:
return error_response(_("Email already exists"))
# Some old data has duplicate email
except MultipleObjectsReturned:
return error_response(_("Email already exists"))
except User.DoesNotExist:
pass
user.username = data["username"]
user.real_name = data["real_name"]
user.email = data["email"]
user.admin_type = data["admin_type"]
user.is_disabled = data["is_disabled"]
if data["password"]:
user.set_password(data["password"])
if data["open_api"]:
# Avoid reset user appkey after saving changes
if not user.open_api:
user.open_api_appkey = rand_str()
else:
user.open_api_appkey = None
user.open_api = data["open_api"]
if data["two_factor_auth"]:
# Avoid reset user tfa_token after saving changes
if not user.two_factor_auth:
user.tfa_token = rand_str()
else:
user.tfa_token = None
user.two_factor_auth = data["two_factor_auth"]
if data["admin_type"] == AdminType.ADMIN:
user.admin_extra_permission = list(set(data["admin_extra_permission"]))
else:
user.admin_extra_permission = []
user.save()
return success_response(UserSerializer(user).data)
else:
return serializer_invalid_response(serializer)
@super_admin_required
def get(self, request):
"""
User list api / Get user by id
"""
user_id = request.GET.get("user_id")
if user_id:
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
return error_response(_("User does not exist"))
return success_response(UserSerializer(user).data)
user = User.objects.all().order_by("-create_time")
admin_type = request.GET.get("admin_type", None)
if admin_type:
try:
user = user.filter(admin_type__gte=int(admin_type))
except ValueError:
return error_response(_("Invalid parameter"))
keyword = request.GET.get("keyword", None)
if keyword:
user = user.filter(Q(username__contains=keyword) |
Q(real_name__contains=keyword) |
Q(email__contains=keyword))
return paginate(request, user, UserSerializer)
def logout(request):
auth.logout(request)
return http.HttpResponseRedirect("/")
@ -83,67 +231,6 @@ def index_page(request):
return http.HttpResponseRedirect('/problems/')
class UserRegisterAPIView(APIView):
def post(self, request):
"""
用户注册json api接口
---
request_serializer: UserRegisterSerializer
"""
serializer = UserRegisterSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(u"验证码错误")
try:
User.objects.get(username=data["username"])
return error_response(u"用户名已存在")
except User.DoesNotExist:
pass
try:
User.objects.get(email=data["email"])
return error_response(u"该邮箱已被注册,请换其他邮箱进行注册")
# 兼容部分老数据,有邮箱重复的
except MultipleObjectsReturned:
return error_response(u"该邮箱已被注册,请换其他邮箱进行注册")
except User.DoesNotExist:
user = User.objects.create(username=data["username"], real_name=data["real_name"],
email=data["email"])
user.set_password(data["password"])
user.save()
UserProfile.objects.create(user=user, school=data["school"], student_id=data["student_id"])
return success_response(u"注册成功!")
else:
return serializer_invalid_response(serializer)
class UserChangePasswordAPIView(APIView):
@login_required
def post(self, request):
"""
用户修改密码json api接口
---
request_serializer: UserChangePasswordSerializer
"""
serializer = UserChangePasswordSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(u"验证码错误")
username = request.user.username
user = auth.authenticate(username=username, password=data["old_password"])
if user:
user.set_password(data["new_password"])
user.save()
return success_response(u"用户密码修改成功!")
else:
return error_response(u"密码不正确,请重新修改!")
else:
return serializer_invalid_response(serializer)
class UsernameCheckAPIView(APIView):
def get(self, request):
"""
@ -186,80 +273,6 @@ class EmailCheckAPIView(APIView):
return Response(status=does_not_existed)
class UserAdminAPIView(APIView):
@super_admin_required
def put(self, request):
"""
用户编辑json api接口
---
request_serializer: EditUserSerializer
response_serializer: UserSerializer
"""
serializer = EditUserSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
user = User.objects.get(id=data["id"])
except User.DoesNotExist:
return error_response(u"该用户不存在!")
try:
user = User.objects.get(username=data["username"])
if user.id != data["id"]:
return error_response(u"昵称已经存在")
except User.DoesNotExist:
pass
user.username = data["username"]
user.real_name = data["real_name"]
user.email = data["email"]
user.admin_type = data["admin_type"]
if data["password"]:
user.set_password(data["password"])
# 后台控制用户是否可以使用openapi
if data["openapi"] is False:
user.openapi_appkey = None
elif data["openapi"] and user.openapi_appkey is None:
user.openapi_appkey = rand_str()
# 后台控制用户是否使用两步验证
# 注意:用户没开启,后台开启的话,用户没有绑定过两步验证token,会造成无法登陆的!
if data["tfa_auth"] is False:
user.two_factor_auth = False
elif data["tfa_auth"] and user.two_factor_auth is False:
user.two_factor_auth = True
user.tfa_token = rand_str()
# 后台控制用户是否被禁用
user.is_forbidden = data["is_forbidden"]
user.save()
return success_response(UserSerializer(user).data)
else:
return serializer_invalid_response(serializer)
@super_admin_required
def get(self, request):
"""
用户分页json api接口
---
response_serializer: UserSerializer
"""
user = User.objects.all().order_by("-create_time")
admin_type = request.GET.get("admin_type", None)
if admin_type:
try:
user = user.filter(admin_type__gte=int(admin_type))
except ValueError:
return error_response(u"参数错误")
keyword = request.GET.get("keyword", None)
if keyword:
user = user.filter(Q(username__contains=keyword) |
Q(real_name__contains=keyword) |
Q(email__contains=keyword))
return paginate(request, user, UserSerializer)
class UserInfoAPIView(APIView):
@login_required
def get(self, request):

View File

@ -95,7 +95,7 @@ WSGI_APPLICATION = 'oj.wsgi.application'
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Shanghai'

View File

@ -35,6 +35,7 @@ urlpatterns = [
url(r'^admin/template/(?P<template_dir>\w+)/(?P<template_name>\w+).html$', AdminTemplateView.as_view(),
name="admin_template"),
# account app
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"),
url(r'^logout/$', "account.views.logout", name="user_logout_api"),
url(r'^register/$', TemplateView.as_view(template_name="oj/account/register.html"),
@ -43,13 +44,14 @@ urlpatterns = [
name="user_change_password_page"),
url(r'^announcement/(?P<announcement_id>\d+)/$', "announcement.views.announcement_page",
name="announcement_page"),
url(r'^api/user/$', UserInfoAPIView.as_view(), name="user_info_api"),
url(r'^api/login/$', UserLoginAPIView.as_view(), name="user_login_api"),
url(r'^api/register/$', UserRegisterAPIView.as_view(), name="user_register_api"),
url(r'^api/change_password/$', UserChangePasswordAPIView.as_view(), name="user_change_password_api"),
url(r'^api/username_check/$', UsernameCheckAPIView.as_view(), name="username_check_api"),
url(r'^api/email_check/$', EmailCheckAPIView.as_view(), name="email_check_api"),
url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
url(r'^api/contest/submission/$', ContestSubmissionAPIView.as_view(), name="contest_submission_api"),
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),

View File

@ -1,9 +1,8 @@
# coding=utf-8
import json
from rest_framework import serializers
from account.models import User
from utils.serializers import JSONField
from .models import Problem, ProblemTag
@ -12,11 +11,6 @@ class ProblemSampleSerializer(serializers.ListField):
output = serializers.CharField(max_length=3000)
class JSONField(serializers.Field):
def to_representation(self, value):
return json.loads(value)
class CreateProblemSerializer(serializers.Serializer):
title = serializers.CharField(max_length=50)
description = serializers.CharField(max_length=10000)

18
utils/serializers.py Normal file
View File

@ -0,0 +1,18 @@
# coding=utf-8
import json
from django.utils import timezone
from rest_framework import serializers
class JSONField(serializers.Field):
def to_representation(self, value):
return json.loads(value)
class DateTimeTZField(serializers.DateTimeField):
def to_representation(self, value):
self.format = "%Y-%-m-%d %-H:%-M:%-S"
value = timezone.localtime(value)
return super(DateTimeTZField, self).to_representation(value)

View File

@ -81,7 +81,7 @@ def paginate_data(request, query_set, object_serializer):
"previous_page": None,
"next_page": None,
"page_size": page_size,
"current_page": page,
"current_page": int(page),
"count": paginator.count,
"total_page": paginator.num_pages}