Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions nextcloudappstore/user/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ def clean_email(self):

def clean_passwd(self):
value = self.cleaned_data.get("passwd")
# If password was entered, validate it
if value and not self.instance.check_password(value):
# If password was entered, validate it. Accounts without a usable
# password (e.g. GitHub-only login) have nothing to check against.
if value and self.instance.has_usable_password() and not self.instance.check_password(value):
raise forms.ValidationError(_("Invalid password"))
return value

Expand Down Expand Up @@ -144,7 +145,7 @@ def clean(self):
if "passwd" in self._errors:
del self._errors["passwd"]

else:
elif self.instance.has_usable_password():
# If critical fields (email, first_name, last_name) changed,
# ensure password was provided and is correct.
# If no password provided or invalid, an error would be present from clean_passwd().
Expand Down
68 changes: 68 additions & 0 deletions nextcloudappstore/user/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from allauth.account.models import EmailAddress
from allauth.core import ratelimit
from allauth.socialaccount.models import SocialAccount
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.test import RequestFactory, TestCase, override_settings
Expand Down Expand Up @@ -184,3 +185,70 @@ def test_budget_shared_with_allauth_email_view(self):
email_url = reverse("account_email")
r = self.client.post(email_url, {"email": "viaemail@nextcloud.com", "action_add": ""})
self.assertEqual(r.status_code, 429)


@override_settings(
ACCOUNT_RATE_LIMITS={
"reset_password": "1/m/ip",
"login_failed": "10/h/ip",
"manage_email": "3/h/user",
},
)
class GitHubOnlyAccountEditTest(TestCase):
"""A user who signed up via GitHub has no usable password, so the
account edit form must not demand one to confirm critical changes."""

@classmethod
def setUpClass(cls):
super().setUpClass()
cls._validate_patcher = patch(
"nextcloudappstore.user.adapters.validate_email",
side_effect=_validate_email_no_dns,
)
cls._validate_patcher.start()

@classmethod
def tearDownClass(cls):
cls._validate_patcher.stop()
super().tearDownClass()

def setUp(self):
cache.clear()
self.user = create_user(username="githubuser", password="unused", email=PRIMARY_EMAIL)
self.user.set_unusable_password()
self.user.save()
SocialAccount.objects.create(user=self.user, provider="github", uid="12345")
self.client.force_login(self.user)
self.url = reverse("user:account")

def test_can_change_name_without_password(self):
response = self.client.post(
self.url,
{
"first_name": "New",
"last_name": "Name",
"email": PRIMARY_EMAIL,
"subscribe_to_news": False,
"passwd": "",
},
)
self.assertEqual(response.status_code, 302)
updated = get_user_model().objects.get(pk=self.user.pk)
self.assertEqual(updated.first_name, "New")
self.assertEqual(updated.last_name, "Name")

def test_can_change_email_without_password(self):
response = self.client.post(
self.url,
{
"first_name": "Test",
"last_name": "User",
"email": "newgithub@nextcloud.com",
"subscribe_to_news": False,
"passwd": "",
},
)
self.assertEqual(response.status_code, 302)
self.assertTrue(
EmailAddress.objects.filter(user=self.user, email="newgithub@nextcloud.com", verified=False).exists()
)
Loading