Repository: ietf-tools/datatracker

Issue:
Sign in link issues
### Describe the issue

The "Sign in" link in the top right-hand corner keeps appending content for the next query parameter.

To reproduce:
* Go to Datatracker as an unlogged user.
* Click on "Sign in" button on the top right-hand corner.
* Click the "Sign in" button in the top right-hand corner again.
* New URL is `https://datatracker.ietf.org/accounts/login/?next=/accounts/login/%3Fnext%3D/`
* If you click again, the URL will be `https://datatracker.ietf.org/accounts/login/?next=/accounts/login/%3Fnext%3D/accounts/login/%253Fnext%253D/`.

Because clicking on the "Sign in" button from the sign-in page changes the `next` query parameter to `/account/login/<stuff>`, the user will be presented with a login screen after successful login.

### Code of Conduct

- [X] I agree to follow the [IETF's Code of Conduct](https://github.com/ietf-tools/.github/blob/main/CODE_OF_CONDUCT.md)


Discussion:
Might be nice to put the page to return to in the session rather than have it as a query parameter (we _know_ there's a session).

Resolving patch:
diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py
--- a/ietf/doc/templatetags/ietf_filters.py
+++ b/ietf/doc/templatetags/ietf_filters.py
@@ -1,4 +1,4 @@
-# Copyright The IETF Trust 2007-2020, All Rights Reserved
+# Copyright The IETF Trust 2007-2023, All Rights Reserved
 # -*- coding: utf-8 -*-
 
 
@@ -409,9 +409,9 @@ def startswith(x, y):
     return str(x).startswith(y)
 
 
-@register.filter(name='removesuffix', is_safe=False)
-def removesuffix(value, suffix):
-    """Remove an exact-match suffix
+@register.filter(name='removeprefix', is_safe=False)
+def removeprefix(value, prefix):
+    """Remove an exact-match prefix
     
     The is_safe flag is False because indiscriminate use of this could result in non-safe output.
     See https://docs.djangoproject.com/en/2.2/howto/custom-template-tags/#filters-and-auto-escaping
@@ -419,8 +419,8 @@ def removesuffix(value, suffix):
     HTML-unsafe output.
     """
     base = str(value)
-    if base.endswith(suffix):
-        return base[:-len(suffix)]
+    if base.startswith(prefix):
+        return base[len(prefix):]
     else:
         return base
 


Test patch:
diff --git a/ietf/ietfauth/tests.py b/ietf/ietfauth/tests.py
--- a/ietf/ietfauth/tests.py
+++ b/ietf/ietfauth/tests.py
@@ -1,4 +1,4 @@
-# Copyright The IETF Trust 2009-2022, All Rights Reserved
+# Copyright The IETF Trust 2009-2023, All Rights Reserved
 # -*- coding: utf-8 -*-
 
 
@@ -111,6 +111,35 @@ def test_login_and_logout(self):
         self.assertEqual(r.status_code, 302)
         self.assertEqual(urlsplit(r["Location"])[2], "/foobar")
 
+    def test_login_button(self):
+        PersonFactory(user__username='plain')
+
+        def _test_login(url):
+            # try mashing the sign-in button repeatedly
+            r = self.client.get(url)
+            if r.status_code == 302:
+                r = self.client.get(r["Location"])
+            self.assertEqual(r.status_code, 200)
+            q = PyQuery(r.content)
+            login_url = q("a:Contains('Sign in')").attr("href")
+            self.assertEqual(login_url, "/accounts/login/?next=" + url)
+            r = self.client.get(login_url)
+            self.assertEqual(r.status_code, 200)
+            q = PyQuery(r.content)
+            login_url = q("a:Contains('Sign in')").attr("href")
+            self.assertEqual(login_url, "/accounts/login/?next=" + url)
+
+            # try logging in with the provided next
+            r = self.client.post(login_url, {"username":"plain", "password":"plain+password"})
+            self.assertEqual(r.status_code, 302)
+            self.assertEqual(urlsplit(r["Location"])[2], url)
+            self.client.logout()
+
+        # try with a trivial next
+        _test_login("/")
+        # try with a next that requires login
+        _test_login(urlreverse(ietf.ietfauth.views.profile))
+
     def test_login_with_different_email(self):
         person = PersonFactory(user__username='plain')
         email = EmailFactory(person=person)