-
-
Notifications
You must be signed in to change notification settings - Fork 144
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #521 from rhenanbartels/fix/auth-backend
Fix/auth backend
- Loading branch information
Showing
19 changed files
with
325 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
from urllib.parse import urlparse | ||
|
||
import environ | ||
import sentry_sdk | ||
from django.urls import reverse_lazy | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
""" | ||
Anteriormente, os usuários podiam cadastrar seu username utilizando o caractere | ||
inválido '@'. | ||
Este script corrige esse problema alterando os usernames que contem '@' por | ||
variações únicas baseadas no nome de usuário e/ou no e-mail de cada registro. | ||
Exemplo: | ||
old_username = 'name@' | ||
new_username = 'name' | ||
""" | ||
import csv | ||
import string | ||
from typing import Tuple | ||
|
||
from django.contrib.auth import get_user_model | ||
from rows.utils import slug | ||
|
||
from brasilio_auth.forms import is_valid_username | ||
|
||
User = get_user_model() | ||
possible_chars = string.ascii_letters + string.digits + "_" | ||
|
||
|
||
def possible_usernames(username: str, email: str, n_suffix: int = 10) -> Tuple[str, ...]: | ||
username = username.strip() | ||
email = email.strip() | ||
|
||
if username.lower() == email.lower(): | ||
username = username.split("@")[0] | ||
else: | ||
if username.startswith("@"): | ||
username = username[1:] | ||
if username.endswith("@"): | ||
username = username[:-1] | ||
if "@" in username: | ||
stop = username.find("@") | ||
before, after = username[:stop], username[stop + 1 :] | ||
if "." in after: | ||
username = before | ||
else: | ||
username = "_".join(username.split("@")) | ||
|
||
email_parts = email.split("@") | ||
possible_2 = email_parts[0] | ||
possible_3 = email_parts[0] + "_" + email_parts[1].split(".")[0] | ||
possible_with_suffix = [f"{username}_{i}" for i in range(1, n_suffix)] | ||
return (username, possible_2, possible_3, *possible_with_suffix) | ||
|
||
|
||
def migrate_usernames(filepath): | ||
with open(filepath, mode="w") as fobj: | ||
writer = csv.DictWriter(fobj, fieldnames=["old_username", "new_username", "email"]) | ||
writer.writeheader() | ||
for user in User.objects.all(): | ||
if is_valid_username(user.username): | ||
continue | ||
|
||
# Define possible usernames based on current and remove any | ||
# non-allowed chars | ||
possible = [ | ||
slug(username, permitted_chars=possible_chars) | ||
for username in possible_usernames(user.username, user.email) | ||
] | ||
for username in possible: | ||
if not User.objects.filter(username=username).exists(): | ||
writer.writerow({"old_username": user.username, "new_username": username, "email": user.email}) | ||
user.username = username | ||
user.save() | ||
break | ||
print(f"ERROR: could not migrate {user} (tried: {', '.join(possible)})") | ||
|
||
|
||
def run(): | ||
filepath = "/data/fixed-usernames.csv" | ||
migrate_usernames(filepath) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,10 @@ | |
|
||
|
||
class UserCreationFormTests(TestCase): | ||
username_invalid_error = "Nome de usuário pode conter apenas letras, números e '_' e não deve ser um documento" | ||
username_max_length_error = "Certifique-se de que o valor tenha no máximo 150 caracteres (ele possui 151)." | ||
username_exists_error = "Nome de usuário já existente (escolha um diferente)." | ||
|
||
def test_required_fields(self): | ||
required_fields = ["username", "email", "password1", "password2", "captcha"] | ||
|
||
|
@@ -42,30 +46,12 @@ def test_create_user(self): | |
assert user.check_password(passwd) is True | ||
assert not NewsletterSubscriber.objects.filter(user=user).exists() | ||
|
||
@patch.object(ReCaptchaField, "validate", Mock(return_value=True)) | ||
def test_force_lower_for_username(self): | ||
passwd = "verygoodpassword" | ||
data = { | ||
"username": "FOO", | ||
"email": "[email protected]", | ||
"password1": passwd, | ||
"password2": passwd, | ||
"captcha": "captcha-validation", | ||
} | ||
|
||
form = UserCreationForm(data) | ||
assert form.is_valid() is True | ||
user = form.save() | ||
user.refresh_from_db() | ||
|
||
assert "foo" == user.username | ||
|
||
@patch.object(ReCaptchaField, "validate", Mock(return_value=True)) | ||
def test_respect_abstract_user_max_length_for_username(self): | ||
passwd = "verygoodpassword" | ||
data = { | ||
"username": "a" * 150, | ||
"email": "foo@bar.com", | ||
"email": "another_foo@bar.com", | ||
"password1": passwd, | ||
"password2": passwd, | ||
"captcha": "captcha-validation", | ||
|
@@ -78,14 +64,16 @@ def test_respect_abstract_user_max_length_for_username(self): | |
form = UserCreationForm(data) | ||
assert not form.is_valid() | ||
assert "username" in form.errors | ||
assert "email" not in form.errors | ||
assert form.errors["username"] == [self.username_max_length_error] | ||
|
||
@patch.object(ReCaptchaField, "validate", Mock(return_value=True)) | ||
def test_invalid_username_if_already_exists(self): | ||
baker.make(settings.AUTH_USER_MODEL, username="foo") | ||
passwd = "verygoodpassword" | ||
data = { | ||
"username": "foo", | ||
"email": "foo@bar.com", | ||
"email": "another_foo@bar.com", | ||
"password1": passwd, | ||
"password2": passwd, | ||
"captcha": "captcha-validation", | ||
|
@@ -94,13 +82,15 @@ def test_invalid_username_if_already_exists(self): | |
form = UserCreationForm(data) | ||
assert form.is_valid() is False | ||
assert "username" in form.errors | ||
assert "email" not in form.errors | ||
assert form.errors["username"] == [self.username_exists_error] | ||
|
||
@patch.object(ReCaptchaField, "validate", Mock(return_value=True)) | ||
def test_invalid_email_if_user_already_exists(self): | ||
baker.make(settings.AUTH_USER_MODEL, email="[email protected]") | ||
baker.make(settings.AUTH_USER_MODEL, email="[email protected]", username="foo") | ||
passwd = "verygoodpassword" | ||
data = { | ||
"username": "foo", | ||
"username": "another_foo", | ||
"email": "[email protected]", | ||
"password1": passwd, | ||
"password2": passwd, | ||
|
@@ -110,13 +100,14 @@ def test_invalid_email_if_user_already_exists(self): | |
form = UserCreationForm(data) | ||
assert not form.is_valid() | ||
assert "email" in form.errors | ||
assert "username" not in form.errors | ||
|
||
@patch.object(ReCaptchaField, "validate", Mock(return_value=True)) | ||
def test_email_validation_does_not_break_if_different_letter_case(self): | ||
baker.make(settings.AUTH_USER_MODEL, email="[email protected]") | ||
baker.make(settings.AUTH_USER_MODEL, email="[email protected]", username="foo") | ||
passwd = "verygoodpassword" | ||
data = { | ||
"username": "foo", | ||
"username": "another_foo", | ||
"email": "[email protected]", | ||
"password1": passwd, | ||
"password2": passwd, | ||
|
@@ -126,6 +117,7 @@ def test_email_validation_does_not_break_if_different_letter_case(self): | |
form = UserCreationForm(data) | ||
assert not form.is_valid() | ||
assert "email" in form.errors | ||
assert "username" not in form.errors | ||
|
||
def test_do_not_validate_if_bad_captcha(self): | ||
passwd = "verygoodpassword" | ||
|
@@ -142,6 +134,54 @@ def test_do_not_validate_if_bad_captcha(self): | |
assert not form.is_valid() | ||
assert "captcha" in form.errors | ||
|
||
@patch.object(ReCaptchaField, "validate", Mock(return_value=True)) | ||
def test_invalid_username_characters(self): | ||
passwd = "verygoodpassword" | ||
data = { | ||
"username": "foo@", | ||
"email": "[email protected]", | ||
"password1": passwd, | ||
"password2": passwd, | ||
"captcha": "captcha-validation", | ||
} | ||
|
||
form = UserCreationForm(data) | ||
assert not form.is_valid() | ||
assert "username" in form.errors | ||
assert form.errors["username"] == [self.username_invalid_error] | ||
|
||
@patch.object(ReCaptchaField, "validate", Mock(return_value=True)) | ||
def test_invalid_username_digits(self): | ||
passwd = "verygoodpassword" | ||
data = { | ||
"username": "123.456.789-01", | ||
"email": "[email protected]", | ||
"password1": passwd, | ||
"password2": passwd, | ||
"captcha": "captcha-validation", | ||
} | ||
|
||
form = UserCreationForm(data) | ||
assert not form.is_valid() | ||
assert "username" in form.errors | ||
assert form.errors["username"] == [self.username_invalid_error] | ||
|
||
@patch.object(ReCaptchaField, "validate", Mock(return_value=True)) | ||
def test_do_not_set_username_to_lowercase(self): | ||
passwd = "verygoodpassword" | ||
username = "Foo" | ||
data = { | ||
"username": username, | ||
"email": "[email protected]", | ||
"password1": passwd, | ||
"password2": passwd, | ||
"captcha": "captcha-validation", | ||
} | ||
|
||
form = UserCreationForm(data) | ||
assert form.is_valid() | ||
assert form.cleaned_data["username"] == username | ||
|
||
|
||
class TestTokenApiManagementForm(TestCase): | ||
def test_required_fields(self): | ||
|
Oops, something went wrong.