Skip to content

Commit

Permalink
Merge branch 'master' into cas-out-of-ustc
Browse files Browse the repository at this point in the history
  • Loading branch information
taoky authored Oct 14, 2023
2 parents cb6fa46 + 2f87fb8 commit d6aee48
Show file tree
Hide file tree
Showing 25 changed files with 523 additions and 65 deletions.
9 changes: 7 additions & 2 deletions frontend/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from server.terms.models import Terms
from server.trigger.models import Trigger
from server.user.models import User
from .models import Page, Account, Code, UstcSnos, UstcEligible, Qa, Credits
from .models import Page, Account, Code, AccountLog, SpecialProfileUsedRecord, Qa, Credits

admin.site.register([Page, Account, Code, UstcSnos, UstcEligible, Qa, Credits])
admin.site.register([Page, Account, Code, Qa, Credits, SpecialProfileUsedRecord])


class PermissionListFilter(admin.SimpleListFilter):
Expand Down Expand Up @@ -57,3 +57,8 @@ def has_delete_permission(self, request, obj=None):

def has_view_permission(self, request, obj=None):
return False


@admin.register(AccountLog)
class AccountLogAdmin(admin.ModelAdmin):
search_fields = ["account__pk", "contents"]
4 changes: 2 additions & 2 deletions frontend/auth_providers/external.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class ExternalGetCodeView(BaseGetCodeView):

def send(self, identity, code):
use_smtp = settings.EXTERNAL_LOGINS[self.provider]['use_smtp']
url = settings.EXTERNAL_LOGINS[self.provider]['url']
key = settings.EXTERNAL_LOGINS[self.provider]['key']
url = settings.EXTERNAL_LOGINS[self.provider].get('url', None)
key = settings.EXTERNAL_LOGINS[self.provider].get('key', None)

if settings.DEBUG or use_smtp:
EmailMessage(
Expand Down
24 changes: 24 additions & 0 deletions frontend/auth_providers/gdou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import timedelta

from django.urls import path

from .base import DomainEmailValidator
from .external import ExternalLoginView, ExternalGetCodeView


class LoginView(ExternalLoginView):
template_context = {'provider_name': '广东海洋大学'}
provider = 'gdou'
group = 'gdou'


class GetCodeView(ExternalGetCodeView):
provider = 'gdou'
duration = timedelta(hours=1)
validate_identity = DomainEmailValidator(['stu.gdou.edu.cn'])


urlpatterns = [
path('gdou/login/', LoginView.as_view()),
path('gdou/get_code/', GetCodeView.as_view()),
]
24 changes: 24 additions & 0 deletions frontend/auth_providers/gdut.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import timedelta

from django.urls import path

from .base import DomainEmailValidator
from .external import ExternalLoginView, ExternalGetCodeView


class LoginView(ExternalLoginView):
template_context = {'provider_name': '广东工业大学'}
provider = 'gdut'
group = 'gdut'


class GetCodeView(ExternalGetCodeView):
provider = 'gdut'
duration = timedelta(hours=1)
validate_identity = DomainEmailValidator(['mail2.gdut.edu.cn'])


urlpatterns = [
path('gdut/login/', LoginView.as_view()),
path('gdut/get_code/', GetCodeView.as_view()),
]
24 changes: 24 additions & 0 deletions frontend/auth_providers/gzhu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import timedelta

from django.urls import path

from .base import DomainEmailValidator
from .external import ExternalLoginView, ExternalGetCodeView


class LoginView(ExternalLoginView):
template_context = {'provider_name': '广州大学'}
provider = 'gzhu'
group = 'gzhu'


class GetCodeView(ExternalGetCodeView):
provider = 'gzhu'
duration = timedelta(hours=1)
validate_identity = DomainEmailValidator(['e.gzhu.edu.cn'])


urlpatterns = [
path('gzhu/login/', LoginView.as_view()),
path('gzhu/get_code/', GetCodeView.as_view()),
]
24 changes: 24 additions & 0 deletions frontend/auth_providers/shu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import timedelta

from django.urls import path

from .base import UserRegexAndDomainEmailValidator
from .external import ExternalLoginView, ExternalGetCodeView


class LoginView(ExternalLoginView):
template_context = {'provider_name': '上海大学'}
provider = 'shu'
group = 'shu'


class GetCodeView(ExternalGetCodeView):
provider = 'shu'
duration = timedelta(hours=1)
validate_identity = UserRegexAndDomainEmailValidator('shu.edu.cn', r'^(?:[a-z]|_|-|\d){3,50}$')


urlpatterns = [
path('shu/login/', LoginView.as_view()),
path('shu/get_code/', GetCodeView.as_view()),
]
24 changes: 24 additions & 0 deletions frontend/auth_providers/sustech.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import timedelta

from django.urls import path

from .base import DomainEmailValidator
from .external import ExternalLoginView, ExternalGetCodeView


class LoginView(ExternalLoginView):
template_context = {'provider_name': '南方科技大学'}
provider = 'sustech'
group = 'sustech'


class GetCodeView(ExternalGetCodeView):
provider = 'sustech'
duration = timedelta(hours=1)
validate_identity = DomainEmailValidator(['sustech.edu.cn', 'mail.sustech.edu.cn'])


urlpatterns = [
path('sustech/login/', LoginView.as_view()),
path('sustech/get_code/', GetCodeView.as_view()),
]
14 changes: 7 additions & 7 deletions frontend/auth_providers/ustc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from typing import Optional

from ..models import UstcSnos
from ..models import AccountLog
from .cas import CASBaseLoginView


Expand Down Expand Up @@ -33,13 +33,13 @@ def to_set(s):
def from_set(vs):
return ','.join(sorted(vs))
try:
o = account.ustcsnos
new_snos = from_set(to_set(o.snos) | {self.sno})
if new_snos != o.snos:
o.snos = new_snos
o = AccountLog.objects.get(account=account, content_type='学号')
new_snos = from_set(to_set(o.contents) | {self.sno})
if new_snos != o.contents:
o.contents = new_snos
o.save()
except UstcSnos.DoesNotExist:
UstcSnos.objects.create(account=account, snos=self.sno)
except AccountLog.DoesNotExist:
AccountLog.objects.create(account=account, contents=f"{self.sno}", content_type='学号')
return account


Expand Down
24 changes: 24 additions & 0 deletions frontend/auth_providers/xmut.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import timedelta

from django.urls import path

from .base import DomainEmailValidator
from .external import ExternalLoginView, ExternalGetCodeView


class LoginView(ExternalLoginView):
template_context = {'provider_name': '厦门理工学院'}
provider = 'xmut'
group = 'xmut'


class GetCodeView(ExternalGetCodeView):
provider = 'xmut'
duration = timedelta(hours=1)
validate_identity = DomainEmailValidator(['s.xmut.edu.cn'])


urlpatterns = [
path('xmut/login/', LoginView.as_view()),
path('xmut/get_code/', GetCodeView.as_view()),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Generated by Django 4.2.5 on 2023-10-12 17:46

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
("frontend", "0006_credits"),
]

operations = [
migrations.CreateModel(
name="AccountLog",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("contents", models.TextField()),
("content_type", models.CharField(default="学号", max_length=32)),
(
"account",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="frontend.account",
),
),
],
),
migrations.CreateModel(
name="SpecialProfileUsedRecord",
fields=[
(
"user",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
serialize=False,
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.RemoveField(
model_name="ustcsnos",
name="account",
),
migrations.DeleteModel(
name="UstcEligible",
),
migrations.DeleteModel(
name="UstcSnos",
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.5 on 2023-10-12 18:06

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("frontend", "0007_accountlog_specialprofileusedrecord_and_more"),
]

operations = [
migrations.AddConstraint(
model_name="accountlog",
constraint=models.UniqueConstraint(
fields=("account", "content_type"),
name="unique_account_log_for_each_type",
),
),
]
28 changes: 23 additions & 5 deletions frontend/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class Account(models.Model):
identity = models.TextField()
user = models.ForeignKey(get_user_model(), models.CASCADE, null=True)

def __str__(self):
return f'User {self.user.pk} ({self.provider}:{self.identity})'

class Meta:
unique_together = ('provider', 'identity')

Expand Down Expand Up @@ -71,14 +74,29 @@ def authenticate(cls, provider, identity, code):
return False


class UstcSnos(models.Model):
account = models.OneToOneField(Account, models.CASCADE, primary_key=True)
snos = models.TextField()
# 记录特殊登录方式(例如 USTC CAS)的用户,其登录方式返回的「可靠」信息
class AccountLog(models.Model):
account = models.OneToOneField(Account, models.CASCADE, db_index=True)
contents = models.TextField()
content_type = models.CharField(max_length=32, default='学号')

def __str__(self):
return f"Account {self.account.pk} ({self.content_type} {self.contents})"

class Meta:
constraints = [
models.UniqueConstraint(fields=['account', 'content_type'],
name='unique_account_log_for_each_type'),
]


class UstcEligible(models.Model):
# 记录需要在首次登录后显示换组页面并且已经换组的用户
# 目前只有 USTC 有这个需求
class SpecialProfileUsedRecord(models.Model):
user = models.OneToOneField(get_user_model(), models.CASCADE, primary_key=True)
eligible = models.BooleanField()

def __str__(self) -> str:
return f"A used record of User id {self.user.pk}"


class Qa(models.Model):
Expand Down
14 changes: 11 additions & 3 deletions frontend/static/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ table {
.content {
margin: 0 auto 2em;
padding: 0 1em;
max-width: 72em;
max-width: 81em;
}
@media screen and (min-width: 48em) {
.content {
Expand Down Expand Up @@ -121,7 +121,7 @@ h1 {
}
@media screen and (min-width: 48em) {
.challenges {
max-width: 25em;
max-width: 28em;
margin-left: 1em;
}
}
Expand Down Expand Up @@ -179,7 +179,7 @@ table .narrow {
}
@media screen and (min-width: 48em) {
.main-body img {
max-width: calc(100% - 26em);
max-width: calc(100% - 28em);
}
.logout {
max-width: 14em;
Expand All @@ -189,3 +189,11 @@ table .narrow {
max-width: 50%;
width: 100%;
}
/* revert changes by purecss in form */
#token {
border: revert;
background-color: revert;
padding: revert;
vertical-align: revert;
border-radius: revert;
}
5 changes: 4 additions & 1 deletion frontend/templates/admin_user.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ <h2 v-else>正在创建</h2>
<input class="vTextField" type="text" id="form-nickname" name="nickname" v-model="opened.nickname" required>
</div>
<div class="form-row">
<a :href="`/admin/auth/user/${opened.pk}/change`">跳转到 Django 用户模型页面……</a>
<a :href="`/admin/auth/user/${opened.pk}/change`">跳转到 Django 用户模型页面(配置权限)……</a>
</div>
<div class="form-row">
<a :href="`/admin/frontend/account/${opened.pk}/change`">跳转到 Account 模型页面(查看登录方式信息)……</a>
</div>
<div class="form-row">
<label for="form-name">姓名:</label>
Expand Down
Loading

0 comments on commit d6aee48

Please sign in to comment.