first working prototype of bitid
This commit is contained in:
parent
a01e5ef393
commit
ed4e085011
|
@ -64,8 +64,12 @@ Add the following settings to your project for the Monero Backend:
|
||||||
``DJCL_MONERO_WALLET_RPC_USER`` and ``DJCL_MONERO_WALLET_RPC_PASS`` to specify
|
``DJCL_MONERO_WALLET_RPC_USER`` and ``DJCL_MONERO_WALLET_RPC_PASS`` to specify
|
||||||
which wallet RPC should be used.
|
which wallet RPC should be used.
|
||||||
|
|
||||||
In case only Bitcoin Backend is used, you just need ``DJCL_MONERO_NETWORK`` with
|
In case only Bitcoin Backend is used, you just need:
|
||||||
|
|
||||||
|
* ``DJCL_BITCOIN_NETWORK`` with
|
||||||
one of two possible values: ``mainnet`` or ``testnet``.
|
one of two possible values: ``mainnet`` or ``testnet``.
|
||||||
|
* Add ``django_cryptolock.backends.BitcoinAddressBackend`` to your
|
||||||
|
``AUTHENTICATION_BACKENDS``
|
||||||
|
|
||||||
Finaly create the templates files (``login.html`` and ``signup.html``) under a
|
Finaly create the templates files (``login.html`` and ``signup.html``) under a
|
||||||
``django_cryptolock`` subfolder.
|
``django_cryptolock`` subfolder.
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import warnings
|
|
||||||
|
|
||||||
from django.contrib.auth.backends import ModelBackend
|
from django.contrib.auth.backends import ModelBackend
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
@ -7,10 +5,9 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from monerorpc.authproxy import AuthServiceProxy, JSONRPCException
|
from monerorpc.authproxy import AuthServiceProxy, JSONRPCException
|
||||||
from pybitid import bitid
|
|
||||||
|
|
||||||
from .models import Address
|
from .models import Address
|
||||||
from .utils import verify_signature
|
from .utils import verify_monero_signature, verify_bitcoin_signature
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -38,7 +35,9 @@ class MoneroAddressBackend(ModelBackend):
|
||||||
if not stored_address:
|
if not stored_address:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
is_valid = verify_signature(address, challenge, signature)
|
is_valid = verify_monero_signature(
|
||||||
|
stored_address.address, challenge, signature
|
||||||
|
)
|
||||||
except JSONRPCException:
|
except JSONRPCException:
|
||||||
raise PermissionDenied(_("Error while validating signature"))
|
raise PermissionDenied(_("Error while validating signature"))
|
||||||
|
|
||||||
|
@ -52,21 +51,14 @@ class BitcoinAddressBackend(ModelBackend):
|
||||||
"""Custom Bitcoin-BitId authentication backend."""
|
"""Custom Bitcoin-BitId authentication backend."""
|
||||||
|
|
||||||
def authenticate(
|
def authenticate(
|
||||||
self, request, address=None, bitid_uri=None, signature=None, **kwargs
|
self, request, address=None, challenge=None, signature=None, **kwargs
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Validates the provided signature for the given Bitcoin address and challenge.
|
Validates the provided signature for the given Bitcoin address and challenge.
|
||||||
|
|
||||||
This method does not rely on any external components, everything is done locally.
|
This method does not rely on any external components, everything is done locally.
|
||||||
"""
|
"""
|
||||||
network = getattr(settings, "DJCL_BITCOIN_NETWORK", None)
|
if not all([address, challenge, signature]):
|
||||||
if not network:
|
|
||||||
warnings.warn(
|
|
||||||
_("Please configure the bitcoin network in the settings file")
|
|
||||||
)
|
|
||||||
is_testnet = True if network == "testnet" else False
|
|
||||||
|
|
||||||
if not all([address, bitid_uri, signature]):
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
stored_address = (
|
stored_address = (
|
||||||
|
@ -77,9 +69,8 @@ class BitcoinAddressBackend(ModelBackend):
|
||||||
if not stored_address:
|
if not stored_address:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
callback_uri = request.build_absolute_uri()
|
valid_signature = verify_bitcoin_signature(
|
||||||
valid_signature = bitid.challenge_valid(
|
stored_address.address, challenge, signature, request
|
||||||
address, signature, bitid_uri, callback_uri, is_testnet
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if valid_signature:
|
if valid_signature:
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.translation import gettext, gettext_lazy as _
|
from django.utils.translation import gettext, gettext_lazy as _
|
||||||
|
|
||||||
|
from pybitid import bitid
|
||||||
|
|
||||||
from .models import Address
|
from .models import Address
|
||||||
from .validators import validate_monero_address
|
from .validators import validate_monero_address
|
||||||
from .utils import generate_challenge
|
from .utils import generate_challenge
|
||||||
|
@ -17,7 +20,9 @@ class ChallengeMixin(forms.Form):
|
||||||
challenge = forms.CharField()
|
challenge = forms.CharField()
|
||||||
|
|
||||||
def include_challange(self):
|
def include_challange(self):
|
||||||
new_challenge = generate_challenge()
|
new_challenge = bitid.build_uri(
|
||||||
|
self.request.build_absolute_uri(), generate_challenge()
|
||||||
|
)
|
||||||
if not self.data:
|
if not self.data:
|
||||||
self.request.session["current_challenge"] = new_challenge
|
self.request.session["current_challenge"] = new_challenge
|
||||||
self.initial["challenge"] = new_challenge
|
self.initial["challenge"] = new_challenge
|
||||||
|
@ -33,7 +38,7 @@ class ChallengeMixin(forms.Form):
|
||||||
class SimpleLoginForm(ChallengeMixin, forms.Form):
|
class SimpleLoginForm(ChallengeMixin, forms.Form):
|
||||||
"""Basic login form, that can be used as reference for implementation."""
|
"""Basic login form, that can be used as reference for implementation."""
|
||||||
|
|
||||||
address = forms.CharField(validators=[validate_monero_address])
|
address = forms.CharField()
|
||||||
signature = forms.CharField()
|
signature = forms.CharField()
|
||||||
|
|
||||||
error_messages = {
|
error_messages = {
|
||||||
|
@ -84,7 +89,7 @@ class SimpleSignUpForm(ChallengeMixin, forms.Form):
|
||||||
"""Basic login form, that can be used as reference for implementation."""
|
"""Basic login form, that can be used as reference for implementation."""
|
||||||
|
|
||||||
username = forms.CharField()
|
username = forms.CharField()
|
||||||
address = forms.CharField(validators=[validate_monero_address])
|
address = forms.CharField()
|
||||||
signature = forms.CharField()
|
signature = forms.CharField()
|
||||||
|
|
||||||
def __init__(self, request=None, *args, **kwargs):
|
def __init__(self, request=None, *args, **kwargs):
|
||||||
|
@ -96,6 +101,13 @@ class SimpleSignUpForm(ChallengeMixin, forms.Form):
|
||||||
|
|
||||||
def clean_address(self):
|
def clean_address(self):
|
||||||
value = self.cleaned_data["address"]
|
value = self.cleaned_data["address"]
|
||||||
|
|
||||||
if Address.objects.filter(address=value).exists():
|
if Address.objects.filter(address=value).exists():
|
||||||
raise forms.ValidationError(_("This address already exists"))
|
raise forms.ValidationError(_("This address already exists"))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def clean_username(self):
|
||||||
|
value = self.cleaned_data["username"]
|
||||||
|
if get_user_model().objects.filter(username=value).exists():
|
||||||
|
raise forms.ValidationError(_("This username is already taken"))
|
||||||
|
return value
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.http.request import HttpRequest
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
@ -8,6 +9,7 @@ from django.core.exceptions import ValidationError
|
||||||
from model_utils.models import TimeStampedModel
|
from model_utils.models import TimeStampedModel
|
||||||
|
|
||||||
from .validators import validate_monero_address, validate_bitcoin_address
|
from .validators import validate_monero_address, validate_bitcoin_address
|
||||||
|
from .utils import verify_bitcoin_signature, verify_monero_signature
|
||||||
|
|
||||||
|
|
||||||
class Address(TimeStampedModel):
|
class Address(TimeStampedModel):
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
from .views import MoneroLoginView, MoneroSignUpView
|
from .views import CryptoLockLoginView, CryptoLockSignUpView
|
||||||
|
|
||||||
|
|
||||||
app_name = "django_cryptolock"
|
app_name = "django_cryptolock"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r"login", MoneroLoginView.as_view(), name="login"),
|
url(r"login", CryptoLockLoginView.as_view(), name="login"),
|
||||||
url(r"signup", MoneroSignUpView.as_view(), name="signup"),
|
url(r"signup", CryptoLockSignUpView.as_view(), name="signup"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
|
import warnings
|
||||||
from secrets import token_hex
|
from secrets import token_hex
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from monerorpc.authproxy import AuthServiceProxy
|
from monerorpc.authproxy import AuthServiceProxy
|
||||||
|
from pybitid import bitid
|
||||||
|
|
||||||
|
|
||||||
def verify_signature(address: str, challenge: str, signature: str) -> bool:
|
def verify_monero_signature(address: str, challenge: str, signature: str) -> bool:
|
||||||
"""Makes a request to wallet RPC to verify address and signature."""
|
"""Makes a request to wallet RPC to verify address and signature."""
|
||||||
protocol = settings.DJCL_MONERO_WALLET_RPC_PROTOCOL
|
protocol = settings.DJCL_MONERO_WALLET_RPC_PROTOCOL
|
||||||
host = settings.DJCL_MONERO_WALLET_RPC_HOST
|
host = settings.DJCL_MONERO_WALLET_RPC_HOST
|
||||||
|
@ -20,6 +24,21 @@ def verify_signature(address: str, challenge: str, signature: str) -> bool:
|
||||||
return result.get("good", False)
|
return result.get("good", False)
|
||||||
|
|
||||||
|
|
||||||
|
def verify_bitcoin_signature(
|
||||||
|
address: str, challenge: str, signature: str, request: HttpRequest
|
||||||
|
) -> bool:
|
||||||
|
"""Verifies if the provided bitcoin signature is valid."""
|
||||||
|
network = getattr(settings, "DJCL_BITCOIN_NETWORK", None)
|
||||||
|
if not network:
|
||||||
|
warnings.warn(_("Please configure the bitcoin network in the settings file"))
|
||||||
|
is_testnet = True if network == "testnet" else False
|
||||||
|
callback_uri = request.build_absolute_uri()
|
||||||
|
|
||||||
|
return bitid.challenge_valid(
|
||||||
|
address, signature, challenge, callback_uri, is_testnet
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def generate_challenge():
|
def generate_challenge():
|
||||||
"""Generates a new random challenge for the authentication."""
|
"""Generates a new random challenge for the authentication."""
|
||||||
return token_hex(8)
|
return token_hex(8)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.contrib.auth.views import LoginView
|
from django.contrib.auth.views import LoginView
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
@ -10,15 +11,16 @@ from django.urls import reverse
|
||||||
from monerorpc.authproxy import JSONRPCException
|
from monerorpc.authproxy import JSONRPCException
|
||||||
|
|
||||||
from .forms import SimpleSignUpForm, SimpleLoginForm
|
from .forms import SimpleSignUpForm, SimpleLoginForm
|
||||||
from .utils import verify_signature
|
from .utils import verify_monero_signature, verify_bitcoin_signature
|
||||||
|
from .models import Address
|
||||||
|
|
||||||
|
|
||||||
class MoneroLoginView(LoginView):
|
class CryptoLockLoginView(LoginView):
|
||||||
template_name = "django_cryptolock/login.html"
|
template_name = "django_cryptolock/login.html"
|
||||||
form_class = SimpleLoginForm
|
form_class = SimpleLoginForm
|
||||||
|
|
||||||
|
|
||||||
class MoneroSignUpView(FormView):
|
class CryptoLockSignUpView(FormView):
|
||||||
template_name = "django_cryptolock/signup.html"
|
template_name = "django_cryptolock/signup.html"
|
||||||
form_class = SimpleSignUpForm
|
form_class = SimpleSignUpForm
|
||||||
|
|
||||||
|
@ -26,19 +28,18 @@ class MoneroSignUpView(FormView):
|
||||||
return self.form_class(request=self.request, **self.get_form_kwargs())
|
return self.form_class(request=self.request, **self.get_form_kwargs())
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
username = form.cleaned_data["username"]
|
|
||||||
address = form.cleaned_data["address"]
|
|
||||||
challenge = form.cleaned_data["challenge"]
|
|
||||||
signature = form.cleaned_data["signature"]
|
|
||||||
try:
|
try:
|
||||||
valid_sig = verify_signature(address, challenge, signature)
|
valid_sig, network = self.verify_signature(form)
|
||||||
except JSONRPCException:
|
except JSONRPCException:
|
||||||
form._errors["__all__"] = ErrorList([_("Error connecting to daemon")])
|
form._errors["__all__"] = ErrorList([_("Error connecting to daemon")])
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
username = form.cleaned_data["username"]
|
||||||
|
address = form.cleaned_data["address"]
|
||||||
|
|
||||||
if valid_sig:
|
if valid_sig:
|
||||||
user = get_user_model().objects.create(username=username)
|
user = get_user_model().objects.create(username=username)
|
||||||
user.address_set.create(address=address)
|
user.address_set.create(address=address, network=network)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
else:
|
else:
|
||||||
form._errors["signature"] = ErrorList([_("Invalid signature")])
|
form._errors["signature"] = ErrorList([_("Invalid signature")])
|
||||||
|
@ -46,3 +47,24 @@ class MoneroSignUpView(FormView):
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return settings.LOGIN_REDIRECT_URL
|
return settings.LOGIN_REDIRECT_URL
|
||||||
|
|
||||||
|
def verify_signature(self, form):
|
||||||
|
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"
|
||||||
|
valid_sig = False
|
||||||
|
network = None
|
||||||
|
|
||||||
|
if bitcoin_backend in settings.AUTHENTICATION_BACKENDS:
|
||||||
|
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:
|
||||||
|
valid_sig = verify_monero_signature(address, challenge, signature)
|
||||||
|
network = Address.NETWORK_MONERO
|
||||||
|
|
||||||
|
return valid_sig, network
|
||||||
|
|
|
@ -116,10 +116,14 @@ LOGIN_REDIRECT_URL = "/"
|
||||||
LOGOUT_REDIRECT_URL = "/"
|
LOGOUT_REDIRECT_URL = "/"
|
||||||
|
|
||||||
|
|
||||||
# Monero Cryptolock Settings
|
# Django Cryptolock Settings
|
||||||
# Wallet RPC
|
# Wallet RPC
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = ["django_cryptolock.backends.MoneroAddressBackend"]
|
AUTHENTICATION_BACKENDS = [
|
||||||
|
"django_cryptolock.backends.BitcoinAddressBackend",
|
||||||
|
"django_cryptolock.backends.MoneroAddressBackend",
|
||||||
|
]
|
||||||
|
DJCL_BITCOIN_NETWORK = "mainnet"
|
||||||
DJCL_MONERO_NETWORK = "mainnet"
|
DJCL_MONERO_NETWORK = "mainnet"
|
||||||
DJCL_MONERO_WALLET_RPC_PROTOCOL = os.environ.get("MONERO_WALLET_RPC_PROTOCOL", "http")
|
DJCL_MONERO_WALLET_RPC_PROTOCOL = os.environ.get("MONERO_WALLET_RPC_PROTOCOL", "http")
|
||||||
DJCL_MONERO_WALLET_RPC_HOST = os.environ.get("MONERO_WALLET_RPC_HOST", "localhost:6000")
|
DJCL_MONERO_WALLET_RPC_HOST = os.environ.get("MONERO_WALLET_RPC_HOST", "localhost:6000")
|
||||||
|
|
|
@ -78,7 +78,7 @@ def test_monero_backend_invalid_signature(settings, existing_user):
|
||||||
set_monero_settings(settings)
|
set_monero_settings(settings)
|
||||||
mommy.make(Address, address=VALID_MONERO_ADDRESS, user=existing_user)
|
mommy.make(Address, address=VALID_MONERO_ADDRESS, user=existing_user)
|
||||||
|
|
||||||
with patch("django_cryptolock.backends.verify_signature") as verify_mock:
|
with patch("django_cryptolock.backends.verify_monero_signature") as verify_mock:
|
||||||
verify_mock.return_value = False
|
verify_mock.return_value = False
|
||||||
user = authenticate(
|
user = authenticate(
|
||||||
MagicMock(),
|
MagicMock(),
|
||||||
|
@ -90,11 +90,11 @@ def test_monero_backend_invalid_signature(settings, existing_user):
|
||||||
assert user is None
|
assert user is None
|
||||||
|
|
||||||
|
|
||||||
def test_monero_backed_valid_signature(settings, existing_user):
|
def test_monero_backend_valid_signature(settings, existing_user):
|
||||||
set_monero_settings(settings)
|
set_monero_settings(settings)
|
||||||
mommy.make(Address, address=VALID_MONERO_ADDRESS, user=existing_user)
|
mommy.make(Address, address=VALID_MONERO_ADDRESS, user=existing_user)
|
||||||
|
|
||||||
with patch("django_cryptolock.backends.verify_signature") as verify_mock:
|
with patch("django_cryptolock.backends.verify_monero_signature") as verify_mock:
|
||||||
verify_mock.return_value = True
|
verify_mock.return_value = True
|
||||||
user = authenticate(
|
user = authenticate(
|
||||||
MagicMock(),
|
MagicMock(),
|
||||||
|
@ -167,7 +167,7 @@ def test_bitcoin_backend_valid_signature(settings, existing_user):
|
||||||
user = authenticate(
|
user = authenticate(
|
||||||
mock,
|
mock,
|
||||||
address=VALID_BITCOIN_ADDRESS,
|
address=VALID_BITCOIN_ADDRESS,
|
||||||
bitid_uri=VALID_BITID_URI,
|
challenge=VALID_BITID_URI,
|
||||||
signature=VALID_BITCOIN_SIG,
|
signature=VALID_BITCOIN_SIG,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,11 @@ def test_simpleloginform_generates_new_challenge():
|
||||||
initial = {}
|
initial = {}
|
||||||
request.session.__setitem__.side_effect = initial.__setitem__
|
request.session.__setitem__.side_effect = initial.__setitem__
|
||||||
request.session.__getitem__.side_effect = initial.__getitem__
|
request.session.__getitem__.side_effect = initial.__getitem__
|
||||||
|
request.build_absolute_uri.return_value = "http://something/"
|
||||||
form = SimpleLoginForm(request=request)
|
form = SimpleLoginForm(request=request)
|
||||||
assert form.initial.get("challenge")
|
assert form.initial.get("challenge")
|
||||||
assert initial["current_challenge"] == form.initial.get("challenge")
|
assert initial["current_challenge"] == form.initial.get("challenge")
|
||||||
|
assert form.initial.get("challenge").startswith("bitid://something")
|
||||||
|
|
||||||
|
|
||||||
def test_simpleloginform_generates_no_new_challenge():
|
def test_simpleloginform_generates_no_new_challenge():
|
||||||
|
@ -29,6 +31,7 @@ def test_simpleloginform_generates_no_new_challenge():
|
||||||
initial = {}
|
initial = {}
|
||||||
request.session.__setitem__.side_effect = initial.__setitem__
|
request.session.__setitem__.side_effect = initial.__setitem__
|
||||||
request.session.__getitem__.side_effect = initial.__getitem__
|
request.session.__getitem__.side_effect = initial.__getitem__
|
||||||
|
request.build_absolute_uri.return_value = "http://something/"
|
||||||
form = SimpleLoginForm(request=request, data={"address": ""})
|
form = SimpleLoginForm(request=request, data={"address": ""})
|
||||||
assert not form.initial.get("challenge")
|
assert not form.initial.get("challenge")
|
||||||
assert not initial.get("current_challenge")
|
assert not initial.get("current_challenge")
|
||||||
|
@ -38,6 +41,7 @@ def test_simpleloginform_generates_no_new_challenge():
|
||||||
def test_simpleloginform_valid_data(settings):
|
def test_simpleloginform_valid_data(settings):
|
||||||
settings.DJCL_MONERO_NETWORK = "mainnet"
|
settings.DJCL_MONERO_NETWORK = "mainnet"
|
||||||
request = MagicMock()
|
request = MagicMock()
|
||||||
|
request.build_absolute_uri.return_value = "http://something/"
|
||||||
form = SimpleLoginForm(
|
form = SimpleLoginForm(
|
||||||
request=request,
|
request=request,
|
||||||
data={
|
data={
|
||||||
|
@ -57,9 +61,11 @@ def test_simplesignupform_generaes_new_challenge():
|
||||||
initial = {}
|
initial = {}
|
||||||
request.session.__setitem__.side_effect = initial.__setitem__
|
request.session.__setitem__.side_effect = initial.__setitem__
|
||||||
request.session.__getitem__.side_effect = initial.__getitem__
|
request.session.__getitem__.side_effect = initial.__getitem__
|
||||||
|
request.build_absolute_uri.return_value = "http://something/"
|
||||||
form = SimpleSignUpForm(request=request)
|
form = SimpleSignUpForm(request=request)
|
||||||
assert form.initial.get("challenge")
|
assert form.initial.get("challenge")
|
||||||
assert initial["current_challenge"] == form.initial.get("challenge")
|
assert initial["current_challenge"] == form.initial.get("challenge")
|
||||||
|
assert form.initial.get("challenge").startswith("bitid://something")
|
||||||
|
|
||||||
|
|
||||||
def test_simplesignupform_generaes_no_new_challenge():
|
def test_simplesignupform_generaes_no_new_challenge():
|
||||||
|
@ -67,6 +73,7 @@ def test_simplesignupform_generaes_no_new_challenge():
|
||||||
initial = {}
|
initial = {}
|
||||||
request.session.__setitem__.side_effect = initial.__setitem__
|
request.session.__setitem__.side_effect = initial.__setitem__
|
||||||
request.session.__getitem__.side_effect = initial.__getitem__
|
request.session.__getitem__.side_effect = initial.__getitem__
|
||||||
|
request.build_absolute_uri.return_value = "http://something/"
|
||||||
form = SimpleSignUpForm(request=request, data={"address": ""})
|
form = SimpleSignUpForm(request=request, data={"address": ""})
|
||||||
assert not form.initial.get("challenge")
|
assert not form.initial.get("challenge")
|
||||||
assert not initial.get("current_challenge")
|
assert not initial.get("current_challenge")
|
||||||
|
@ -76,6 +83,7 @@ def test_validate_address_unique(settings):
|
||||||
settings.DJCL_MONERO_NETWORK = "mainnet"
|
settings.DJCL_MONERO_NETWORK = "mainnet"
|
||||||
mommy.make(Address, address=VALID_ADDRESS)
|
mommy.make(Address, address=VALID_ADDRESS)
|
||||||
request = MagicMock()
|
request = MagicMock()
|
||||||
|
request.build_absolute_uri.return_value = "http://something/"
|
||||||
form = SimpleSignUpForm(
|
form = SimpleSignUpForm(
|
||||||
request=request,
|
request=request,
|
||||||
data={
|
data={
|
||||||
|
|
Loading…
Reference in New Issue