add config for invite-only mode

This commit is contained in:
Your Name 2023-09-03 20:11:46 +00:00 committed by Henri Dickson
parent 86fc011be7
commit 35b7993e48
10 changed files with 103 additions and 3 deletions

View file

@ -91,6 +91,7 @@ SETUP_ADMIN_USERNAMES = [
u for u in os.environ.get("NEODB_ADMIN_USERNAMES", "").split(",") if u
]
INVITE_ONLY = os.environ.get("NEODB_INVITE_ONLY", "") != ""
# Mastodon/Pleroma instance allowed to login, keep empty to allow any instance to login
MASTODON_ALLOWED_SITES = []

View file

@ -7,6 +7,7 @@ from django.core.paginator import Paginator
from django.db.models import Count
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.decorators.http import require_http_methods
@ -272,6 +273,8 @@ def discover(request):
# gallery["items"] = Item.objects.filter(id__in=ids)
if request.user.is_authenticated:
if not request.user.registration_complete:
return redirect(reverse("users:register"))
layout = request.user.preference.discover_layout
identity = request.user.identity
podcast_ids = [

View file

@ -11,11 +11,15 @@ from users.models import User
@login_required
def me(request):
if not request.user.registration_complete:
return redirect(reverse("users:register"))
return redirect(request.user.identity.url)
def home(request):
if request.user.is_authenticated:
if not request.user.registration_complete:
return redirect(reverse("users:register"))
home = request.user.preference.classic_homepage
if home == 1:
return redirect(request.user.url)

View file

@ -21,6 +21,7 @@ x-shared:
NEODB_DEBUG:
NEODB_SECRET_KEY:
NEODB_ADMIN_USERNAMES:
NEODB_INVITE_ONLY:
NEODB_DB_NAME: neodb
NEODB_DB_USER: neodb
NEODB_DB_PASSWORD: aubergine

View file

@ -2,7 +2,8 @@ import logging
from django.contrib.auth.decorators import login_required
from django.core.exceptions import BadRequest
from django.shortcuts import render
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from catalog.models import *
@ -19,6 +20,8 @@ PAGE_SIZE = 10
def feed(request):
if request.method != "GET":
raise BadRequest()
if not request.user.registration_complete:
return redirect(reverse("users:register"))
user = request.user
podcast_ids = [
p.item_id

View file

@ -1,5 +1,6 @@
import datetime
import os
import random
import re
import secrets
import ssl
@ -137,6 +138,51 @@ class RsaKeys:
return private_key_serialized, public_key_serialized
class Invite(models.Model):
"""
An invite token, good for one signup.
"""
class Meta:
# managed = False
db_table = "users_invite"
# Should always be lowercase
token = models.CharField(max_length=500, unique=True)
# Admin note about this code
note = models.TextField(null=True, blank=True)
# Uses remaining (null means "infinite")
uses = models.IntegerField(null=True, blank=True)
# Expiry date
expires = models.DateTimeField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
@classmethod
def create_random(cls, uses=None, expires=None, note=None):
return cls.objects.create(
token="".join(
random.choice("abcdefghkmnpqrstuvwxyz23456789") for i in range(20)
),
uses=uses,
expires=expires,
note=note,
)
@property
def valid(self):
if self.uses is not None:
if self.uses <= 0:
return False
if self.expires is not None:
return self.expires >= timezone.now()
return True
class User(AbstractBaseUser):
identities: "RelatedManager[Identity]"

View file

@ -592,3 +592,10 @@ class Takahe:
)
cache.set(cache_key, peers, timeout=1800)
return peers
@staticmethod
def verify_invite(token):
if not token:
return False
invite = Invite.objects.filter(token=token).first()
return invite and invite.valid

View file

@ -25,6 +25,7 @@ from journal.models import remove_data_by_user
from mastodon import mastodon_request_included
from mastodon.api import *
from mastodon.api import verify_account
from takahe.utils import Takahe
from .models import Preference, User
from .tasks import *
@ -49,7 +50,13 @@ def login(request):
# store redirect url in the cookie
if request.GET.get("next"):
request.session["next_url"] = request.GET.get("next")
invite_status = -1 if settings.INVITE_ONLY else 0
if settings.INVITE_ONLY and request.GET.get("invite"):
if Takahe.verify_invite(request.GET.get("invite")):
invite_status = 1
request.session["invite"] = request.GET.get("invite")
else:
invite_status = -2
return render(
request,
"users/login.html",
@ -58,6 +65,7 @@ def login(request):
"scope": quote(settings.MASTODON_CLIENT_SCOPE),
"selected_site": selected_site,
"allow_any_site": settings.MASTODON_ALLOW_ANY_SITE,
"invite_status": invite_status,
},
)
else:
@ -188,6 +196,18 @@ def OAuth2_login(request):
def register_new_user(request, **param):
if settings.INVITE_ONLY:
if not Takahe.verify_invite(request.session.get("invite")):
return render(
request,
"common/error.html",
{
"msg": _("注册失败😫"),
"secondary_msg": _("本站仅限邀请注册"),
},
)
else:
del request.session["invite"]
new_user = User.register(**param)
request.session["new_user"] = True
auth_login(request, new_user)

View file

@ -203,6 +203,10 @@ class User(AbstractUser):
def __str__(self):
return f'{self.pk}:{self.username or ""}:{self.mastodon_acct}'
@property
def registration_complete(self):
return self.username is not None
def clear(self):
if self.mastodon_site == "removed" and not self.is_active:
return

View file

@ -132,13 +132,24 @@
});
</script>
{% else %}
<select name="domain" placeholder="test">
<select name="domain">
{% for site in sites %}
<option value="{{ site.domain_name }}" data-client-id="{{ site.client_id }}">@{{ site.domain_name }}</option>
{% endfor %}
</select>
<input type='submit' value="{% trans '授权登录' %}" id="loginButton" />
{% endif %}
{% if invite_status %}
<small>
{% if invite_status == 1 %}
<i class="fa-solid fa-circle-check"></i> 邀请链接有效,可注册新用户
{% elif invite_status == -1 %}
<i class="fa-solid fa-person-circle-question"></i> 本站目前为邀请注册,已有账户可直接登入,新用户请使用有效邀请链接注册
{% elif invite_status == -2 %}
<i class="fa-solid fa-circle-xmark"></i> 邀请链接无效,已有账户可直接登入,新用户请使用有效邀请链接注册
{% endif %}
</small>
{% endif %}
</form>
{% endif %}
<div class="delayed">部分模块加载超时,请检查网络(翻墙)设置。</div>