validate the address first on signup

This commit is contained in:
Gonçalo Valério 2020-03-31 13:59:07 +01:00
parent c2fa8309fd
commit dd2e1a769a
5 changed files with 110 additions and 33 deletions

View File

@ -1,12 +1,14 @@
from django import forms
from django.contrib.auth import authenticate
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.utils.translation import gettext, gettext_lazy as _
from django.conf import settings
from pybitid import bitid
from .models import Address
from .validators import validate_monero_address
from .validators import validate_monero_address, validate_bitcoin_address
from .utils import generate_challenge
@ -98,12 +100,34 @@ class SimpleSignUpForm(ChallengeMixin, forms.Form):
super().__init__(*args, **kwargs)
self.request = request
self.include_challange()
self.network = None
def clean_address(self):
self.network = None
value = self.cleaned_data["address"]
bitcoin_backend = "django_cryptolock.backends.BitcoinAddressBackend"
monero_backend = "django_cryptolock.backends.MoneroAddressBackend"
if bitcoin_backend in settings.AUTHENTICATION_BACKENDS:
try:
validate_bitcoin_address(value)
self.network = Address.NETWORK_BITCOIN
except ValidationError:
pass
if monero_backend in settings.AUTHENTICATION_BACKENDS:
try:
validate_monero_address(value)
self.network = Address.NETWORK_MONERO
except ValidationError:
pass
if not self.network:
raise forms.ValidationError(_("Invalid address"))
if Address.objects.filter(address=value).exists():
raise forms.ValidationError(_("This address already exists"))
return value
def clean_username(self):

View File

@ -29,9 +29,11 @@ class CryptoLockSignUpView(FormView):
def form_valid(self, form):
try:
valid_sig, network = self.verify_signature(form)
valid_sig = self.verify_signature(form)
except JSONRPCException:
form._errors["__all__"] = ErrorList([_("Error connecting to daemon")])
form._errors["__all__"] = ErrorList(
[_("Error connecting to Monero daemon")]
)
return self.form_invalid(form)
username = form.cleaned_data["username"]
@ -39,7 +41,7 @@ class CryptoLockSignUpView(FormView):
if valid_sig:
user = get_user_model().objects.create(username=username)
user.address_set.create(address=address, network=network)
user.address_set.create(address=address, network=form.network)
return super().form_valid(form)
else:
form._errors["signature"] = ErrorList([_("Invalid signature")])
@ -52,19 +54,15 @@ class CryptoLockSignUpView(FormView):
address = form.cleaned_data["address"]
challenge = form.cleaned_data["challenge"]
signature = form.cleaned_data["signature"]
bitcoin_backend = "django_cryptolock.backends.BitcoinAddressBackend"
monero_backend = "django_cryptolock.backends.MoneroAddressBackend"
bitcoin = form.network == Address.NETWORK_BITCOIN
monero = form.network == Address.NETWORK_MONERO
valid_sig = False
network = None
if bitcoin_backend in settings.AUTHENTICATION_BACKENDS:
if bitcoin:
valid_sig = verify_bitcoin_signature(
address, challenge, signature, request=self.request
)
network = Address.NETWORK_BITCOIN
if monero_backend in settings.AUTHENTICATION_BACKENDS and not valid_sig:
elif monero:
valid_sig = verify_monero_signature(address, challenge, signature)
network = Address.NETWORK_MONERO
return valid_sig, network
return valid_sig

13
tests/helpers.py Normal file
View File

@ -0,0 +1,13 @@
def set_monero_settings(settings):
settings.AUTHENTICATION_BACKENDS = [
"django_cryptolock.backends.MoneroAddressBackend",
"django.contrib.auth.backends.ModelBackend",
]
def set_bitcoin_settings(settings):
settings.AUTHENTICATION_BACKENDS = [
"django_cryptolock.backends.BitcoinAddressBackend",
"django.contrib.auth.backends.ModelBackend",
]

View File

@ -9,6 +9,8 @@ from model_mommy import mommy
from django_cryptolock.models import Address
from .helpers import set_monero_settings, set_bitcoin_settings
User = get_user_model()
pytestmark = pytest.mark.django_db
@ -20,20 +22,6 @@ VALID_BITID_URI = "bitid://www.django-cryptolock.test/?x=44d91949c7b2eb20"
EXAMPLE_LOGIN_URL = "https://www.django-cryptolock.test/"
def set_monero_settings(settings):
settings.AUTHENTICATION_BACKENDS = [
"django_cryptolock.backends.MoneroAddressBackend",
"django.contrib.auth.backends.ModelBackend",
]
def set_bitcoin_settings(settings):
settings.AUTHENTICATION_BACKENDS = [
"django_cryptolock.backends.BitcoinAddressBackend",
"django.contrib.auth.backends.ModelBackend",
]
@pytest.fixture
def existing_user():
return User.objects.create_user(**DUMMY_CREDS)

View File

@ -8,9 +8,12 @@ from model_mommy import mommy
from django_cryptolock.forms import SimpleLoginForm, SimpleSignUpForm
from django_cryptolock.models import Address
from .helpers import set_monero_settings, set_bitcoin_settings
pytestmark = pytest.mark.django_db
VALID_ADDRESS = "46fYuhPAdsxMbEeMg97LhSbFPamdiCw7C6b19VEcZSmV6xboWFZuZQ9MTbj1wLszhUExHi63CMtsWjDTrRDqegZiPVebgYq"
VALID_MONERO_ADDRESS = "46fYuhPAdsxMbEeMg97LhSbFPamdiCw7C6b19VEcZSmV6xboWFZuZQ9MTbj1wLszhUExHi63CMtsWjDTrRDqegZiPVebgYq"
VALID_BITCOIN_ADDRESS = "1N5attoW1FviYGnLmRu9xjaPMKTkWxtUCW"
User = get_user_model()
@ -45,7 +48,7 @@ def test_simpleloginform_valid_data(settings):
form = SimpleLoginForm(
request=request,
data={
"address": VALID_ADDRESS,
"address": VALID_MONERO_ADDRESS,
"challenge": "12345678",
"signature": "some valid signature",
},
@ -56,7 +59,7 @@ def test_simpleloginform_valid_data(settings):
assert form.is_valid()
def test_simplesignupform_generaes_new_challenge():
def test_simplesignupform_generates_new_challenge():
request = MagicMock()
initial = {}
request.session.__setitem__.side_effect = initial.__setitem__
@ -68,7 +71,7 @@ def test_simplesignupform_generaes_new_challenge():
assert form.initial.get("challenge").startswith("bitid://something")
def test_simplesignupform_generaes_no_new_challenge():
def test_simplesignupform_generates_no_new_challenge():
request = MagicMock()
initial = {}
request.session.__setitem__.side_effect = initial.__setitem__
@ -81,17 +84,68 @@ def test_simplesignupform_generaes_no_new_challenge():
def test_validate_address_unique(settings):
settings.DJCL_MONERO_NETWORK = "mainnet"
mommy.make(Address, address=VALID_ADDRESS)
mommy.make(Address, address=VALID_MONERO_ADDRESS)
request = MagicMock()
request.build_absolute_uri.return_value = "http://something/"
form = SimpleSignUpForm(
request=request,
data={
"username": "foo",
"address": VALID_ADDRESS,
"address": VALID_MONERO_ADDRESS,
"challenge": "12345678",
"signature": "some valid signature",
},
)
assert not form.is_valid()
assert "This address already exists" in form.errors["address"]
def test_simplesignupform_validate_bitcoin_addr(settings):
set_bitcoin_settings(settings)
request = MagicMock()
request.build_absolute_uri.return_value = "http://something/"
request.session.get.return_value = "12345678"
form = SimpleSignUpForm(
request=request,
data={
"username": "foo",
"address": VALID_BITCOIN_ADDRESS,
"challenge": "12345678",
"signature": "some valid signature",
},
)
assert form.is_valid()
def test_simplesignupform_valid_monero_addr(settings):
set_monero_settings(settings)
settings.DJCL_MONERO_NETWORK = "mainnet"
request = MagicMock()
request.build_absolute_uri.return_value = "http://something/"
request.session.get.return_value = "12345678"
form = SimpleSignUpForm(
request=request,
data={
"username": "foo",
"address": VALID_MONERO_ADDRESS,
"challenge": "12345678",
"signature": "some valid signature",
},
)
assert form.is_valid()
def test_simplesignupform_validate_invalid_addr():
request = MagicMock()
request.build_absolute_uri.return_value = "http://something/"
form = SimpleSignUpForm(
request=request,
data={
"username": "foo",
"address": "bad addr",
"challenge": "12345678",
"signature": "some valid signature",
},
)
assert not form.is_valid()
assert "Invalid address" in form.errors["address"]