Add first implementation of the authentication backend
This commit is contained in:
parent
903e55f75e
commit
6da3033fdf
|
@ -3,4 +3,5 @@ include CONTRIBUTING.rst
|
||||||
include HISTORY.rst
|
include HISTORY.rst
|
||||||
include LICENSE
|
include LICENSE
|
||||||
include README.rst
|
include README.rst
|
||||||
|
include requirements.txt
|
||||||
recursive-include django_cryptolock *.html *.png *.gif *js *.css *jpg *jpeg *svg *py
|
recursive-include django_cryptolock *.html *.png *.gif *js *.css *jpg *jpeg *svg *py
|
||||||
|
|
|
@ -1,6 +1,40 @@
|
||||||
from django.contrib.auth.backends import ModelBackend
|
from django.contrib.auth.backends import ModelBackend
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from monerorpc.authproxy import AuthServiceProxy, JSONRPCException
|
||||||
|
|
||||||
|
from .models import Address
|
||||||
|
from .utils import verify_signature
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
class MoneroAddressBackend(ModelBackend):
|
class MoneroAddressBackend(ModelBackend):
|
||||||
|
"""Custom Monero-Cryptolock authentication backend."""
|
||||||
|
|
||||||
def authenticate(self, request, address=None, challenge=None, signature=None):
|
def authenticate(self, request, address=None, challenge=None, signature=None):
|
||||||
pass
|
"""Validates the provided signature for the given address and challenge.
|
||||||
|
|
||||||
|
This method currently relies on Wallet RPC access to verify the signature,
|
||||||
|
in the future it should be done locally to be more reliable and more
|
||||||
|
performant.
|
||||||
|
"""
|
||||||
|
if not all(address, challenge, signature):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
stored_address = Address.objects.get(address=address).select_related("user")
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
is_valid = verify_signature(address, challenge, signature)
|
||||||
|
except JSONRPCException:
|
||||||
|
raise PermissionDenied(_("Error while validating signature"))
|
||||||
|
|
||||||
|
if is_valid:
|
||||||
|
return stored_address.user
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 2.2.5 on 2019-10-15 16:41
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('django_cryptolock', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='address',
|
||||||
|
unique_together={('user', 'address')},
|
||||||
|
),
|
||||||
|
]
|
|
@ -20,6 +20,7 @@ class Address(TimeStampedModel):
|
||||||
|
|
||||||
verbose_name = _("Address")
|
verbose_name = _("Address")
|
||||||
verbose_name_plural = _("Addresses")
|
verbose_name_plural = _("Addresses")
|
||||||
|
unique_together = ["user", "address"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Unicode representation of Address."""
|
"""Unicode representation of Address."""
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from monerorpc.authproxy import AuthServiceProxy
|
||||||
|
|
||||||
|
|
||||||
|
def verify_signature(address: str, challenge: str, signature: str) -> bool:
|
||||||
|
"""Makes a request to wallet RPC to verify address and signature."""
|
||||||
|
protocol = settings.MONERO_WALLET_RPC_PROTOCOL
|
||||||
|
host = settings.MONERO_WALLET_RPC_HOST
|
||||||
|
user = settings.MONERO_WALLET_RPC_USER
|
||||||
|
pwd = settings.MONERO_WALLET_RPC_PASS
|
||||||
|
wallet_rpc = AuthServiceProxy(f"{protocol}://{user}:{pwd}@{host}/json_rpc")
|
||||||
|
|
||||||
|
result = wallet_rpc.verify(
|
||||||
|
{"data": challenge, "address": address, "signature": signature}
|
||||||
|
)
|
||||||
|
|
||||||
|
return result.get("good", False)
|
|
@ -1,3 +1,4 @@
|
||||||
django>=2.2
|
django>=2.2
|
||||||
django-model-utils>=2.0
|
django-model-utils>=2.0
|
||||||
monero>=0.6
|
monero>=0.6
|
||||||
|
python-monerorpc>=0.5.5
|
||||||
|
|
|
@ -26,3 +26,7 @@ if django.VERSION >= (1, 10):
|
||||||
MIDDLEWARE = ()
|
MIDDLEWARE = ()
|
||||||
else:
|
else:
|
||||||
MIDDLEWARE_CLASSES = ()
|
MIDDLEWARE_CLASSES = ()
|
||||||
|
|
||||||
|
MONERO_WALLET_RPC_HOST = "localhost:3030"
|
||||||
|
MONERO_WALLET_RPC_USER = "test"
|
||||||
|
MONERO_WALLET_RPC_PASS = "test"
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -7,8 +7,8 @@ setenv =
|
||||||
PYTHONPATH = {toxinidir}:{toxinidir}/django_cryptolock
|
PYTHONPATH = {toxinidir}:{toxinidir}/django_cryptolock
|
||||||
commands = coverage run --source django_cryptolock runtests.py
|
commands = coverage run --source django_cryptolock runtests.py
|
||||||
deps =
|
deps =
|
||||||
django-20: Django>=2.2,<3.0
|
django-22: Django>=2.2
|
||||||
-r{toxinidir}/requirements_test.txt
|
-r {toxinidir}/requirements_test.txt
|
||||||
basepython =
|
basepython =
|
||||||
py37: python3.7
|
py37: python3.7
|
||||||
py36: python3.6
|
py36: python3.6
|
||||||
|
|
Loading…
Reference in New Issue