diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 686ac8b1..152c2d76 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -35,7 +35,7 @@ class CustomForm(ModelForm): class LoginForm(CustomForm): class Meta: model = models.User - fields = ['username', 'password'] + fields = ['localname', 'password'] help_texts = {f: None for f in fields} widgets = { 'password': PasswordInput(), @@ -45,7 +45,7 @@ class LoginForm(CustomForm): class RegisterForm(CustomForm): class Meta: model = models.User - fields = ['username', 'email', 'password'] + fields = ['localname', 'email', 'password'] help_texts = {f: None for f in fields} widgets = { 'password': PasswordInput() diff --git a/bookwyrm/migrations/0030_auto_20201224_1939.py b/bookwyrm/migrations/0030_auto_20201224_1939.py new file mode 100644 index 00000000..6de5d37f --- /dev/null +++ b/bookwyrm/migrations/0030_auto_20201224_1939.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.7 on 2020-12-24 19:39 + +import bookwyrm.models.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('bookwyrm', '0029_auto_20201221_2014'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='localname', + field=models.CharField(max_length=255, null=True, unique=True, validators=[bookwyrm.models.fields.validate_localname]), + ), + ] diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 6dd3b496..c6571ff4 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -26,11 +26,20 @@ def validate_remote_id(value): ) +def validate_localname(value): + ''' make sure localnames look okay ''' + if not re.match(r'^[A-Za-z\-_\.0-9]+$', value): + raise ValidationError( + _('%(value)s is not a valid username'), + params={'value': value}, + ) + + def validate_username(value): ''' make sure usernames look okay ''' - if not re.match(r'^[A-Za-z\-_\.]+$', value): + if not re.match(r'^[A-Za-z\-_\.0-9]+@[A-Za-z\-_\.0-9]+\.[a-z]{2,}$', value): raise ValidationError( - _('%(value)s is not a valid remote_id'), + _('%(value)s is not a valid username'), params={'value': value}, ) @@ -147,7 +156,7 @@ class RemoteIdField(ActivitypubFieldMixin, models.CharField): class UsernameField(ActivitypubFieldMixin, models.CharField): ''' activitypub-aware username field ''' - def __init__(self, activitypub_field='preferredUsername'): + def __init__(self, activitypub_field='preferredUsername', **kwargs): self.activitypub_field = activitypub_field # I don't totally know why pylint is mad at this, but it makes it work super( #pylint: disable=bad-super-call diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index f9120ff0..4cbe387f 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -1,4 +1,5 @@ ''' database schema for user data ''' +import re from urllib.parse import urlparse from django.apps import apps @@ -13,6 +14,7 @@ from bookwyrm.models.status import Status, Review from bookwyrm.settings import DOMAIN from bookwyrm.signatures import create_key_pair from bookwyrm.tasks import app +from bookwyrm.utils import regex from .base_model import OrderedCollectionPageMixin from .base_model import ActivitypubMixin, BookWyrmModel from .federated_server import FederatedServer @@ -49,7 +51,8 @@ class User(OrderedCollectionPageMixin, AbstractUser): localname = models.CharField( max_length=255, null=True, - unique=True + unique=True, + validators=[fields.validate_localname], ) # name is your display name, which you can change at will name = fields.CharField(max_length=100, null=True, blank=True) @@ -167,20 +170,17 @@ class User(OrderedCollectionPageMixin, AbstractUser): def save(self, *args, **kwargs): ''' populate fields for new local users ''' # this user already exists, no need to populate fields - if self.id: - return super().save(*args, **kwargs) - - if not self.local: + if not self.local and not re.match(regex.full_username, self.username): # generate a username that uses the domain (webfinger format) actor_parts = urlparse(self.remote_id) self.username = '%s@%s' % (self.username, actor_parts.netloc) return super().save(*args, **kwargs) + if self.id or not self.local: + return super().save(*args, **kwargs) + # populate fields for local users - self.remote_id = 'https://%s/user/%s' % (DOMAIN, self.username) - self.localname = self.username - self.username = '%s@%s' % (self.username, DOMAIN) - self.actor = self.remote_id + self.remote_id = 'https://%s/user/%s' % (DOMAIN, self.localname) self.inbox = '%s/inbox' % self.remote_id self.shared_inbox = 'https://%s/inbox' % DOMAIN self.outbox = '%s/outbox' % self.remote_id diff --git a/bookwyrm/templates/layout.html b/bookwyrm/templates/layout.html index 2e8fdcac..a2565840 100644 --- a/bookwyrm/templates/layout.html +++ b/bookwyrm/templates/layout.html @@ -119,15 +119,15 @@ {% else %}