Skip to content
Merged
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
9 changes: 4 additions & 5 deletions hypha/apply/users/templates/users/activation/email.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@

{% if site %}{{ site.root_url }}{% else %}{{ base_url }}{% endif %}{{ activation_path }}

{% blocktrans %}This link can be used only once and will lead you to a page where you can set your password. It will remain active for {{ timeout_minutes }} minutes, so please set your password as soon as possible.{% endblocktrans %}
{% blocktrans %}This link can be used only once and will log you in. It will remain active for {{ timeout_minutes }} minutes, so please activate your account as soon as possible.{% endblocktrans %}

{% trans "After setting your password, you will be able to log in at" %}: {% if site %}{{ site.root_url }}{% else %}{{ base_url }}{% endif %} {% trans "in the future using" %}:
{% blocktrans %}In the future you can log in at{% endblocktrans %}: {% if site %}{{ site.root_url }}{% else %}{{ base_url }}{% endif %}{% url 'users:passwordless_login_signup' %}

{% trans "Username" %}: {{ username }}
{% trans "Password" %}: {% trans "Your chosen password" %}
{% blocktrans %}Enter your email address ({{ username }}) and we will send you a link that logs you in — no password needed. You can set a password later from your account settings if you prefer.{% endblocktrans %}

{% blocktrans %}If you do not complete the activation process within {{ timeout_minutes }} minutes you can use the password reset form at{% endblocktrans %}: {% if site %}{{ site.root_url }}{% else %}{{ base_url }}{% endif %}{% url 'users:password_reset' %}
{% blocktrans %}If you do not complete the activation process within {{ timeout_minutes }} minutes, just request a new login link from the address above.{% endblocktrans %}

{% blocktrans %}Kind Regards,
The {{ ORG_SHORT_NAME }} Team{% endblocktrans %}
Expand Down
8 changes: 7 additions & 1 deletion hypha/apply/users/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ def test_activation_email_includes_link(self):
send_activation_email(UserFactory())
assert len(mail.outbox) == 1
email_body = mail.outbox[0].body
assert "password reset form at: https://primary-test-host.org" in email_body
assert "https://primary-test-host.org/account/activate/" in email_body

def test_activation_email_points_to_passwordless_login(self):
send_activation_email(UserFactory())
email_body = mail.outbox[0].body
assert "log in at: https://primary-test-host.org/auth/" in email_body
assert "no password needed" in email_body


class TestGetUserByEmail(TestCase):
Expand Down
12 changes: 8 additions & 4 deletions hypha/apply/users/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.conf import settings
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.core import mail
from django.test import TestCase
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
Expand Down Expand Up @@ -166,7 +166,6 @@ def test_get_invalid_uid_shows_invalid_page(self):
def test_post_valid_token_logs_user_in(self):
response = self.client.post(self.url, follow=True)
self.assertEqual(response.status_code, 200)
# Should redirect to create_password page
self.assertTrue(response.wsgi_request.user.is_authenticated)

def test_post_invalid_token_shows_invalid_page(self):
Expand All @@ -177,14 +176,19 @@ def test_post_invalid_token_shows_invalid_page(self):
response = self.client.post(url)
self.assertTemplateUsed(response, "users/activation/invalid.html")

def test_post_valid_token_redirects_to_set_password(self):
def test_post_valid_token_redirects_to_dashboard(self):
response = self.client.post(self.url)
self.assertRedirects(
response,
reverse("users:activate_password"),
reverse("dashboard:dashboard"),
fetch_redirect_response=False,
)

@override_settings(ENFORCE_TWO_FACTOR=True)
def test_post_valid_token_redirects_to_two_factor_setup(self):
response = self.client.post(self.url)
self.assertIn(reverse("two_factor:setup"), response["Location"])

def test_post_valid_token_with_next_includes_redirect(self):
url = self.url + "?next=/dashboard/"
response = self.client.post(url)
Expand Down
22 changes: 15 additions & 7 deletions hypha/apply/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.contrib import messages
from django.contrib.auth import get_user_model, login, update_session_auth_hash
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.forms import AdminPasswordChangeForm
from django.contrib.auth.forms import SetPasswordForm
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.contrib.auth.views import INTERNAL_RESET_SESSION_TOKEN
from django.contrib.auth.views import (
Expand Down Expand Up @@ -340,10 +340,18 @@ def post(self, request, *args, **kwargs):
if self.valid(user, kwargs.get("token")):
user.backend = settings.CUSTOM_AUTH_BACKEND
login(request, user)
url = reverse("users:activate_password")

if redirect_url := get_redirect_url(request, self.redirect_field_name):
url = f"{url}?next={redirect_url}"
return redirect(url)
return redirect(redirect_url)

# If 2FA is enabled, redirect to setup page instead of dashboard
if settings.ENFORCE_TWO_FACTOR:
return redirect(
reverse("two_factor:setup")
+ f"?next={reverse('dashboard:dashboard')}"
)

return redirect("dashboard:dashboard")

return render(request, "users/activation/invalid.html")

Expand Down Expand Up @@ -371,15 +379,15 @@ def get_user(self, uidb64):

def create_password(request):
"""
A custom view for the admin password change form used for account activation.
A custom view for setting a password, used for account activation.
"""
redirect_url = get_redirect_url(request, redirect_field="next")

if request.user.is_anonymous:
raise PermissionDenied()

if request.method == "POST":
form = AdminPasswordChangeForm(request.user, request.POST)
form = SetPasswordForm(request.user, request.POST)

if form.is_valid():
user = form.save()
Expand All @@ -391,7 +399,7 @@ def create_password(request):
else:
messages.error(request, _("Please correct the errors below."))
else:
form = AdminPasswordChangeForm(request.user)
form = SetPasswordForm(request.user)

return render(
request,
Expand Down