2018-07-22 11:05:43 +00:00
|
|
|
import re
|
|
|
|
|
|
|
|
from django.core import validators
|
|
|
|
from django.utils.deconstruct import deconstructible
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
|
2018-05-06 09:30:41 +00:00
|
|
|
from rest_auth.serializers import PasswordResetSerializer as PRS
|
2019-09-21 14:11:08 +00:00
|
|
|
from rest_auth.registration.serializers import RegisterSerializer as RS, get_adapter
|
2018-06-10 08:55:16 +00:00
|
|
|
from rest_framework import serializers
|
2018-07-13 12:10:39 +00:00
|
|
|
|
2018-02-25 13:44:00 +00:00
|
|
|
from funkwhale_api.activity import serializers as activity_serializers
|
2020-01-23 15:38:04 +00:00
|
|
|
from funkwhale_api.common import models as common_models
|
2020-03-18 10:57:33 +00:00
|
|
|
from funkwhale_api.common import preferences
|
2019-01-02 11:39:00 +00:00
|
|
|
from funkwhale_api.common import serializers as common_serializers
|
2020-01-23 10:09:52 +00:00
|
|
|
from funkwhale_api.common import utils as common_utils
|
2019-09-21 14:20:49 +00:00
|
|
|
from funkwhale_api.federation import models as federation_models
|
2020-03-18 10:57:33 +00:00
|
|
|
from funkwhale_api.moderation import models as moderation_models
|
|
|
|
from funkwhale_api.moderation import tasks as moderation_tasks
|
|
|
|
from funkwhale_api.moderation import utils as moderation_utils
|
|
|
|
|
2019-04-23 09:14:58 +00:00
|
|
|
from . import adapters
|
2017-06-23 21:00:42 +00:00
|
|
|
from . import models
|
|
|
|
|
|
|
|
|
2018-07-22 11:05:43 +00:00
|
|
|
@deconstructible
|
|
|
|
class ASCIIUsernameValidator(validators.RegexValidator):
|
|
|
|
regex = r"^[\w]+$"
|
|
|
|
message = _(
|
|
|
|
"Enter a valid username. This value may contain only English letters, "
|
|
|
|
"numbers, and _ characters."
|
|
|
|
)
|
|
|
|
flags = re.ASCII
|
|
|
|
|
|
|
|
|
|
|
|
username_validators = [ASCIIUsernameValidator()]
|
2020-01-23 10:09:52 +00:00
|
|
|
NOOP = object()
|
2018-07-22 11:05:43 +00:00
|
|
|
|
|
|
|
|
2018-06-19 20:23:22 +00:00
|
|
|
class RegisterSerializer(RS):
|
|
|
|
invitation = serializers.CharField(
|
|
|
|
required=False, allow_null=True, allow_blank=True
|
|
|
|
)
|
|
|
|
|
2020-03-18 10:57:33 +00:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.approval_enabled = preferences.get("moderation__signup_approval_enabled")
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if self.approval_enabled:
|
|
|
|
customization = preferences.get("moderation__signup_form_customization")
|
|
|
|
self.fields[
|
|
|
|
"request_fields"
|
|
|
|
] = moderation_utils.get_signup_form_additional_fields_serializer(
|
|
|
|
customization
|
|
|
|
)
|
|
|
|
|
2018-06-19 20:23:22 +00:00
|
|
|
def validate_invitation(self, value):
|
|
|
|
if not value:
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
2018-06-21 17:41:40 +00:00
|
|
|
return models.Invitation.objects.open().get(code__iexact=value)
|
2018-06-19 20:23:22 +00:00
|
|
|
except models.Invitation.DoesNotExist:
|
|
|
|
raise serializers.ValidationError("Invalid invitation code")
|
|
|
|
|
2019-09-21 14:11:08 +00:00
|
|
|
def validate(self, validated_data):
|
|
|
|
data = super().validate(validated_data)
|
|
|
|
# we create a fake user obj with validated data so we can validate
|
|
|
|
# password properly (we have a password validator that requires
|
|
|
|
# a user object)
|
|
|
|
user = models.User(username=data["username"], email=data["email"])
|
|
|
|
get_adapter().clean_password(data["password1"], user)
|
|
|
|
return data
|
|
|
|
|
2019-09-21 14:20:49 +00:00
|
|
|
def validate_username(self, value):
|
|
|
|
username = super().validate_username(value)
|
|
|
|
duplicates = federation_models.Actor.objects.local().filter(
|
|
|
|
preferred_username__iexact=username
|
|
|
|
)
|
|
|
|
if duplicates.exists():
|
|
|
|
raise serializers.ValidationError(
|
|
|
|
"A user with that username already exists."
|
|
|
|
)
|
|
|
|
return username
|
|
|
|
|
2018-06-19 20:23:22 +00:00
|
|
|
def save(self, request):
|
|
|
|
user = super().save(request)
|
2020-03-18 10:57:33 +00:00
|
|
|
update_fields = ["actor"]
|
|
|
|
user.actor = models.create_actor(user)
|
|
|
|
user_request = None
|
|
|
|
if self.approval_enabled:
|
|
|
|
# manually approve users
|
|
|
|
user.is_active = False
|
|
|
|
user_request = moderation_models.UserRequest.objects.create(
|
|
|
|
submitter=user.actor,
|
|
|
|
type="signup",
|
|
|
|
metadata=self.validated_data.get("request_fields", None) or None,
|
|
|
|
)
|
|
|
|
update_fields.append("is_active")
|
2018-06-19 20:23:22 +00:00
|
|
|
if self.validated_data.get("invitation"):
|
|
|
|
user.invitation = self.validated_data.get("invitation")
|
2020-03-18 10:57:33 +00:00
|
|
|
update_fields.append("invitation")
|
|
|
|
user.save(update_fields=update_fields)
|
|
|
|
if user_request:
|
|
|
|
common_utils.on_commit(
|
|
|
|
moderation_tasks.user_request_handle.delay,
|
|
|
|
user_request_id=user_request.pk,
|
|
|
|
new_status=user_request.status,
|
|
|
|
)
|
2018-07-22 10:20:16 +00:00
|
|
|
|
2018-06-19 20:23:22 +00:00
|
|
|
return user
|
|
|
|
|
|
|
|
|
2018-02-25 13:44:00 +00:00
|
|
|
class UserActivitySerializer(activity_serializers.ModelSerializer):
|
|
|
|
type = serializers.SerializerMethodField()
|
2018-06-09 13:36:16 +00:00
|
|
|
name = serializers.CharField(source="username")
|
|
|
|
local_id = serializers.CharField(source="username")
|
2018-02-25 13:44:00 +00:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = models.User
|
2018-06-09 13:36:16 +00:00
|
|
|
fields = ["id", "local_id", "name", "type"]
|
2018-02-25 13:44:00 +00:00
|
|
|
|
|
|
|
def get_type(self, obj):
|
2018-06-09 13:36:16 +00:00
|
|
|
return "Person"
|
2018-02-25 13:44:00 +00:00
|
|
|
|
|
|
|
|
2018-07-17 11:09:13 +00:00
|
|
|
class UserBasicSerializer(serializers.ModelSerializer):
|
2020-02-05 14:06:07 +00:00
|
|
|
avatar = common_serializers.AttachmentSerializer(source="get_avatar")
|
2018-07-17 11:09:13 +00:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = models.User
|
|
|
|
fields = ["id", "username", "name", "date_joined", "avatar"]
|
|
|
|
|
|
|
|
|
2018-03-03 10:20:21 +00:00
|
|
|
class UserWriteSerializer(serializers.ModelSerializer):
|
2020-01-23 10:09:52 +00:00
|
|
|
summary = common_serializers.ContentSerializer(required=False, allow_null=True)
|
2020-01-23 15:38:04 +00:00
|
|
|
avatar = common_serializers.RelatedField(
|
|
|
|
"uuid",
|
|
|
|
queryset=common_models.Attachment.objects.all().local().attached(False),
|
|
|
|
serializer=None,
|
|
|
|
queryset_filter=lambda qs, context: qs.filter(
|
|
|
|
actor=context["request"].user.actor
|
|
|
|
),
|
|
|
|
write_only=True,
|
|
|
|
)
|
2018-07-13 12:10:39 +00:00
|
|
|
|
2018-03-03 10:20:21 +00:00
|
|
|
class Meta:
|
|
|
|
model = models.User
|
2019-09-23 09:30:25 +00:00
|
|
|
fields = [
|
|
|
|
"name",
|
|
|
|
"privacy_level",
|
|
|
|
"avatar",
|
|
|
|
"instance_support_message_display_date",
|
|
|
|
"funkwhale_support_message_display_date",
|
2020-01-23 10:09:52 +00:00
|
|
|
"summary",
|
2019-09-23 09:30:25 +00:00
|
|
|
]
|
2018-03-03 10:20:21 +00:00
|
|
|
|
2020-01-23 10:09:52 +00:00
|
|
|
def update(self, obj, validated_data):
|
|
|
|
if not obj.actor:
|
|
|
|
obj.create_actor()
|
|
|
|
summary = validated_data.pop("summary", NOOP)
|
2020-01-23 15:38:04 +00:00
|
|
|
avatar = validated_data.pop("avatar", NOOP)
|
|
|
|
|
2020-01-23 10:09:52 +00:00
|
|
|
obj = super().update(obj, validated_data)
|
|
|
|
|
|
|
|
if summary != NOOP:
|
|
|
|
common_utils.attach_content(obj.actor, "summary_obj", summary)
|
2020-01-23 15:38:04 +00:00
|
|
|
if avatar != NOOP:
|
|
|
|
obj.actor.attachment_icon = avatar
|
|
|
|
obj.actor.save(update_fields=["attachment_icon"])
|
2020-01-23 10:09:52 +00:00
|
|
|
return obj
|
|
|
|
|
2018-03-03 10:20:21 +00:00
|
|
|
|
|
|
|
class UserReadSerializer(serializers.ModelSerializer):
|
2017-06-23 21:00:42 +00:00
|
|
|
|
|
|
|
permissions = serializers.SerializerMethodField()
|
2019-02-28 08:31:04 +00:00
|
|
|
full_username = serializers.SerializerMethodField()
|
2020-02-05 14:06:07 +00:00
|
|
|
avatar = common_serializers.AttachmentSerializer(source="get_avatar")
|
2017-06-23 21:00:42 +00:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = models.User
|
|
|
|
fields = [
|
2018-06-09 13:36:16 +00:00
|
|
|
"id",
|
|
|
|
"username",
|
2019-02-28 08:31:04 +00:00
|
|
|
"full_username",
|
2018-06-09 13:36:16 +00:00
|
|
|
"name",
|
|
|
|
"email",
|
|
|
|
"is_staff",
|
|
|
|
"is_superuser",
|
|
|
|
"permissions",
|
|
|
|
"date_joined",
|
|
|
|
"privacy_level",
|
2018-07-13 12:10:39 +00:00
|
|
|
"avatar",
|
2017-06-23 21:00:42 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
def get_permissions(self, o):
|
2018-05-18 16:48:46 +00:00
|
|
|
return o.get_permissions()
|
2018-05-06 09:30:41 +00:00
|
|
|
|
2019-02-28 08:31:04 +00:00
|
|
|
def get_full_username(self, o):
|
|
|
|
if o.actor:
|
|
|
|
return o.actor.full_username
|
|
|
|
|
2018-05-06 09:30:41 +00:00
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
class MeSerializer(UserReadSerializer):
|
|
|
|
quota_status = serializers.SerializerMethodField()
|
2020-01-23 10:09:52 +00:00
|
|
|
summary = serializers.SerializerMethodField()
|
2018-09-06 18:35:02 +00:00
|
|
|
|
|
|
|
class Meta(UserReadSerializer.Meta):
|
2019-09-23 09:30:25 +00:00
|
|
|
fields = UserReadSerializer.Meta.fields + [
|
|
|
|
"quota_status",
|
|
|
|
"instance_support_message_display_date",
|
|
|
|
"funkwhale_support_message_display_date",
|
2020-01-23 10:09:52 +00:00
|
|
|
"summary",
|
2019-09-23 09:30:25 +00:00
|
|
|
]
|
2018-09-06 18:35:02 +00:00
|
|
|
|
|
|
|
def get_quota_status(self, o):
|
|
|
|
return o.get_quota_status() if o.actor else 0
|
|
|
|
|
2020-01-23 10:09:52 +00:00
|
|
|
def get_summary(self, o):
|
|
|
|
if not o.actor or not o.actor.summary_obj:
|
|
|
|
return
|
|
|
|
return common_serializers.ContentSerializer(o.actor.summary_obj).data
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
|
2018-05-06 09:30:41 +00:00
|
|
|
class PasswordResetSerializer(PRS):
|
|
|
|
def get_email_options(self):
|
2019-04-23 09:14:58 +00:00
|
|
|
return {"extra_email_context": adapters.get_email_context()}
|
2019-09-21 14:20:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
class UserDeleteSerializer(serializers.Serializer):
|
|
|
|
password = serializers.CharField()
|
|
|
|
confirm = serializers.BooleanField()
|
|
|
|
|
|
|
|
def validate_password(self, value):
|
|
|
|
if not self.instance.check_password(value):
|
|
|
|
raise serializers.ValidationError("Invalid password")
|
|
|
|
|
|
|
|
def validate_confirm(self, value):
|
|
|
|
if not value:
|
|
|
|
raise serializers.ValidationError("Please confirm deletion")
|
|
|
|
return value
|