Add first implementation of the authentication backend

This commit is contained in:
Gonçalo Valério 2019-10-15 17:45:18 +01:00
parent 903e55f75e
commit 6da3033fdf
8 changed files with 81 additions and 3 deletions

View File

@ -3,4 +3,5 @@ include CONTRIBUTING.rst
include HISTORY.rst
include LICENSE
include README.rst
include requirements.txt
recursive-include django_cryptolock *.html *.png *.gif *js *.css *jpg *jpeg *svg *py

View File

@ -1,6 +1,40 @@
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):
"""Custom Monero-Cryptolock authentication backend."""
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

View File

@ -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')},
),
]

View File

@ -20,6 +20,7 @@ class Address(TimeStampedModel):
verbose_name = _("Address")
verbose_name_plural = _("Addresses")
unique_together = ["user", "address"]
def __str__(self):
"""Unicode representation of Address."""

View File

@ -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)

View File

@ -1,3 +1,4 @@
django>=2.2
django-model-utils>=2.0
monero>=0.6
python-monerorpc>=0.5.5

View File

@ -26,3 +26,7 @@ if django.VERSION >= (1, 10):
MIDDLEWARE = ()
else:
MIDDLEWARE_CLASSES = ()
MONERO_WALLET_RPC_HOST = "localhost:3030"
MONERO_WALLET_RPC_USER = "test"
MONERO_WALLET_RPC_PASS = "test"

View File

@ -7,8 +7,8 @@ setenv =
PYTHONPATH = {toxinidir}:{toxinidir}/django_cryptolock
commands = coverage run --source django_cryptolock runtests.py
deps =
django-20: Django>=2.2,<3.0
-r{toxinidir}/requirements_test.txt
django-22: Django>=2.2
-r {toxinidir}/requirements_test.txt
basepython =
py37: python3.7
py36: python3.6