mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2025-01-01 10:02:01 +00:00
Use signals to save ip, user_agent, last_login in sessions
This commit is contained in:
parent
f55a242ec0
commit
a3ca8b2336
@ -1,6 +1,6 @@
|
|||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- "3.5"
|
- "3.6"
|
||||||
install:
|
install:
|
||||||
- sudo apt-get install -qq redis-server && redis-server &
|
- sudo apt-get install -qq redis-server && redis-server &
|
||||||
- pip install -r deploy/requirements.txt
|
- pip install -r deploy/requirements.txt
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
default_app_config = 'account.apps.ProfilesConfig'
|
9
account/apps.py
Normal file
9
account/apps.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ProfilesConfig(AppConfig):
|
||||||
|
name = "account"
|
||||||
|
verbose_name = "account"
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
import account.signals
|
21
account/migrations/0006_user_session_keys.py
Normal file
21
account/migrations/0006_user_session_keys.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.4 on 2017-09-16 06:22
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import jsonfield.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0005_auto_20170830_1154'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='session_keys',
|
||||||
|
field=jsonfield.fields.JSONField(default=[]),
|
||||||
|
),
|
||||||
|
]
|
@ -35,6 +35,7 @@ class User(AbstractBaseUser):
|
|||||||
auth_token = models.CharField(max_length=40, null=True)
|
auth_token = models.CharField(max_length=40, null=True)
|
||||||
two_factor_auth = models.BooleanField(default=False)
|
two_factor_auth = models.BooleanField(default=False)
|
||||||
tfa_token = models.CharField(max_length=40, null=True)
|
tfa_token = models.CharField(max_length=40, null=True)
|
||||||
|
session_keys = JSONField(default=[])
|
||||||
# open api key
|
# open api key
|
||||||
open_api = models.BooleanField(default=False)
|
open_api = models.BooleanField(default=False)
|
||||||
open_api_appkey = models.CharField(max_length=35, null=True)
|
open_api_appkey = models.CharField(max_length=35, null=True)
|
||||||
|
21
account/signals.py
Normal file
21
account/signals.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from django.utils.timezone import now
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(user_logged_in)
|
||||||
|
def add_user_session(sender, request, user, **kwargs):
|
||||||
|
request.session["ip"] = request.META.get('REMOTE_ADDR', '')
|
||||||
|
request.session["user_agent"] = request.META.get('HTTP_USER_AGENT', '')
|
||||||
|
request.session["last_login"] = now()
|
||||||
|
if request.session.session_key not in user.session_keys:
|
||||||
|
user.session_keys.append(request.session.session_key)
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(user_logged_out)
|
||||||
|
def delete_user_session(sender, request, user, **kwargs):
|
||||||
|
# user may be None
|
||||||
|
if user and request.session.session_key in user.session_keys:
|
||||||
|
user.session_keys.remove(request.session.session_key)
|
||||||
|
user.save()
|
@ -194,7 +194,6 @@ class AdminUserTest(APITestCase):
|
|||||||
resp_data = response.data["data"]
|
resp_data = response.data["data"]
|
||||||
self.assertEqual(resp_data["username"], self.username)
|
self.assertEqual(resp_data["username"], self.username)
|
||||||
self.assertEqual(resp_data["email"], "test@qq.com")
|
self.assertEqual(resp_data["email"], "test@qq.com")
|
||||||
self.assertEqual(resp_data["real_name"], "test_name")
|
|
||||||
self.assertEqual(resp_data["open_api"], True)
|
self.assertEqual(resp_data["open_api"], True)
|
||||||
self.assertEqual(resp_data["two_factor_auth"], False)
|
self.assertEqual(resp_data["two_factor_auth"], False)
|
||||||
self.assertEqual(resp_data["is_disabled"], False)
|
self.assertEqual(resp_data["is_disabled"], False)
|
||||||
|
@ -39,7 +39,6 @@ class UserAdminAPI(APIView):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
user.username = data["username"]
|
user.username = data["username"]
|
||||||
user.real_name = data["real_name"]
|
|
||||||
user.email = data["email"]
|
user.email = data["email"]
|
||||||
user.admin_type = data["admin_type"]
|
user.admin_type = data["admin_type"]
|
||||||
user.is_disabled = data["is_disabled"]
|
user.is_disabled = data["is_disabled"]
|
||||||
|
@ -5,6 +5,7 @@ from otpauth import OtpAuth
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
|
from importlib import import_module
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
@ -267,8 +268,8 @@ class ApplyResetPasswordAPI(APIView):
|
|||||||
user = User.objects.get(email=data["email"])
|
user = User.objects.get(email=data["email"])
|
||||||
except User.DoesNotExist:
|
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 \
|
if user.reset_password_token_expire_time and 0 < int(
|
||||||
0 < int((user.reset_password_token_expire_time - now()).total_seconds()) < 20 * 60:
|
(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 = rand_str()
|
||||||
user.reset_password_token_expire_time = now() + timedelta(minutes=20)
|
user.reset_password_token_expire_time = now() + timedelta(minutes=20)
|
||||||
@ -278,7 +279,7 @@ class ApplyResetPasswordAPI(APIView):
|
|||||||
"website_name": config.name,
|
"website_name": config.name,
|
||||||
"link": f"{config.base_url}/reset-password/{user.reset_password_token}"
|
"link": f"{config.base_url}/reset-password/{user.reset_password_token}"
|
||||||
}
|
}
|
||||||
email_html = render_to_string('reset_password_email.html', render_data)
|
email_html = render_to_string("reset_password_email.html", render_data)
|
||||||
send_email_async.delay(config.name,
|
send_email_async.delay(config.name,
|
||||||
user.email,
|
user.email,
|
||||||
user.username,
|
user.username,
|
||||||
|
@ -33,10 +33,11 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'rest_framework',
|
||||||
|
|
||||||
'account',
|
'account',
|
||||||
'announcement',
|
'announcement',
|
||||||
@ -45,8 +46,6 @@ INSTALLED_APPS = (
|
|||||||
'contest',
|
'contest',
|
||||||
'utils',
|
'utils',
|
||||||
'submission',
|
'submission',
|
||||||
|
|
||||||
'rest_framework',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
MIDDLEWARE_CLASSES = (
|
||||||
@ -54,14 +53,14 @@ MIDDLEWARE_CLASSES = (
|
|||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'account.middleware.AdminRoleRequiredMiddleware',
|
'account.middleware.AdminRoleRequiredMiddleware',
|
||||||
'account.middleware.SessionSecurityMiddleware',
|
'account.middleware.SessionSecurityMiddleware',
|
||||||
|
# 'account.middleware.LogSqlMiddleware',
|
||||||
)
|
)
|
||||||
|
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||||
ROOT_URLCONF = 'oj.urls'
|
ROOT_URLCONF = 'oj.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
|
@ -147,7 +147,7 @@ class ProblemAPITest(APITestCase):
|
|||||||
|
|
||||||
def test_get_problem_list(self):
|
def test_get_problem_list(self):
|
||||||
self.create_problem()
|
self.create_problem()
|
||||||
resp = self.client.get(self.url)
|
resp = self.client.get(f"{self.url}?limit=10")
|
||||||
self.assertSuccess(resp)
|
self.assertSuccess(resp)
|
||||||
|
|
||||||
def get_one_problem(self):
|
def get_one_problem(self):
|
||||||
|
@ -6,6 +6,7 @@ from ..serializers import ProblemSerializer, TagSerializer
|
|||||||
from ..serializers import ContestProblemSerializer
|
from ..serializers import ContestProblemSerializer
|
||||||
from contest.models import ContestRuleType
|
from contest.models import ContestRuleType
|
||||||
|
|
||||||
|
|
||||||
class ProblemTagAPI(APIView):
|
class ProblemTagAPI(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
return self.success(TagSerializer(ProblemTag.objects.all(), many=True).data)
|
return self.success(TagSerializer(ProblemTag.objects.all(), many=True).data)
|
||||||
@ -22,6 +23,10 @@ class ProblemAPI(APIView):
|
|||||||
except Problem.DoesNotExist:
|
except Problem.DoesNotExist:
|
||||||
return self.error("Problem does not exist")
|
return self.error("Problem does not exist")
|
||||||
|
|
||||||
|
limit = request.GET.get("limit")
|
||||||
|
if not limit:
|
||||||
|
return self.error("Limit is needed")
|
||||||
|
|
||||||
problems = Problem.objects.select_related("created_by").filter(visible=True)
|
problems = Problem.objects.select_related("created_by").filter(visible=True)
|
||||||
# 按照标签筛选
|
# 按照标签筛选
|
||||||
tag_text = request.GET.get("tag")
|
tag_text = request.GET.get("tag")
|
||||||
@ -61,12 +66,14 @@ class ContestProblemAPI(APIView):
|
|||||||
problem_id = request.GET.get("problem_id")
|
problem_id = request.GET.get("problem_id")
|
||||||
if problem_id:
|
if problem_id:
|
||||||
try:
|
try:
|
||||||
problem = ContestProblem.objects.select_related("created_by").get(_id=problem_id, contest=self.contest, visible=True)
|
problem = ContestProblem.objects.select_related("created_by").get(_id=problem_id, contest=self.contest,
|
||||||
|
visible=True)
|
||||||
except ContestProblem.DoesNotExist:
|
except ContestProblem.DoesNotExist:
|
||||||
return self.error("Problem does not exist.")
|
return self.error("Problem does not exist.")
|
||||||
return self.success(ContestProblemSerializer(problem).data)
|
return self.success(ContestProblemSerializer(problem).data)
|
||||||
|
|
||||||
contest_problems = ContestProblem.objects.select_related("created_by").filter(contest=self.contest, visible=True)
|
contest_problems = ContestProblem.objects.select_related("created_by").filter(contest=self.contest,
|
||||||
|
visible=True)
|
||||||
# 根据profile, 为做过的题目添加标记
|
# 根据profile, 为做过的题目添加标记
|
||||||
data = ContestProblemSerializer(contest_problems, many=True).data
|
data = ContestProblemSerializer(contest_problems, many=True).data
|
||||||
if request.user.id:
|
if request.user.id:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
from account.decorators import login_required, check_contest_permission
|
from account.decorators import login_required, check_contest_permission
|
||||||
from judge.tasks import judge_task
|
from judge.tasks import judge_task
|
||||||
from judge.dispatcher import JudgeDispatcher
|
# from judge.dispatcher import JudgeDispatcher
|
||||||
from problem.models import Problem, ProblemRuleType, ContestProblem
|
from problem.models import Problem, ProblemRuleType, ContestProblem
|
||||||
from contest.models import Contest, ContestStatus
|
from contest.models import Contest, ContestStatus
|
||||||
from utils.api import APIView, validate_serializer
|
from utils.api import APIView, validate_serializer
|
||||||
|
@ -15,7 +15,6 @@ import os
|
|||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from io import BytesIO
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
from base64 import b64encode
|
|
||||||
|
|
||||||
from . import Captcha
|
from . import Captcha
|
||||||
from ..api import APIView
|
from ..api import APIView
|
||||||
from ..shortcuts import img2base64
|
from ..shortcuts import img2base64
|
||||||
|
Loading…
Reference in New Issue
Block a user