mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 12:11:35 +00:00
Create user generation API
Endpoint request contains a token. Server checks whether it has enough entropy to be secure (at least 128 bits and >0.7 shannon heterogeneity). Server hashes the token (once as of now) and derives nickname and robohash avatar (goes into static/assets/avatars). Nickname is limited to only adjectives, nouns and 3 digits, restricted to 18 characters long max. The corpus of nicknames is 20 billion big (maybe not enough? nick collision chance can happen, but server will just reply with 400 unless the token matches perfectly too)
This commit is contained in:
parent
f0ac03e519
commit
354e7fc5c7
3
.gitignore
vendored
3
.gitignore
vendored
@ -638,3 +638,6 @@ FodyWeavers.xsd
|
||||
# Django
|
||||
*migrations*
|
||||
frontend/static/frontend/main*
|
||||
|
||||
# robosats
|
||||
frontend/static/assets/avatars*
|
||||
|
@ -1,7 +1,8 @@
|
||||
from django.urls import path
|
||||
from .views import MakeOrder, OrderView
|
||||
from .views import MakeOrder, OrderView, UserGenerator
|
||||
|
||||
urlpatterns = [
|
||||
path('make/', MakeOrder.as_view()),
|
||||
path('order/', OrderView.as_view()),
|
||||
path('usergen/', UserGenerator.as_view()),
|
||||
]
|
53
api/views.py
53
api/views.py
@ -1,7 +1,7 @@
|
||||
from rest_framework import status
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from django.contrib.auth.backends import BaseBackend
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .serializers import OrderSerializer, MakeOrderSerializer
|
||||
@ -9,6 +9,11 @@ from .models import Order
|
||||
|
||||
from .nick_generator.nick_generator import NickGenerator
|
||||
from robohash import Robohash
|
||||
from scipy.stats import entropy
|
||||
from math import log2
|
||||
import numpy as np
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
|
||||
# Create your views here.
|
||||
|
||||
@ -93,13 +98,55 @@ class UserGenerator(APIView):
|
||||
|
||||
def get(self,request):
|
||||
'''
|
||||
Get a new user based on high entropy token
|
||||
Get a new user derived from a high entropy token
|
||||
|
||||
- Request has a high-entropy token,
|
||||
- Generates new nickname and avatar.
|
||||
- Creates login credentials (new User object)
|
||||
Response with Avatar and Nickname.
|
||||
'''
|
||||
pass
|
||||
token = request.GET.get(self.lookup_url_kwarg)
|
||||
|
||||
# Compute token entropy
|
||||
value, counts = np.unique(list(token), return_counts=True)
|
||||
shannon_entropy = entropy(counts, base=62)
|
||||
bits_entropy = log2(len(value)**len(token))
|
||||
|
||||
# Start preparing payload
|
||||
context = {'token_shannon_entropy': shannon_entropy, 'token_bits_entropy': bits_entropy}
|
||||
|
||||
# Deny user gen if entropy below 128 bits or 0.7 shannon heterogeneity
|
||||
if bits_entropy < 128 or shannon_entropy < 0.7:
|
||||
context['Bad Request'] = 'The token does not have enough entropy'
|
||||
return Response(context, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Hashes the token, only 1 iteration. Maybe more is better.
|
||||
hash = hashlib.sha256(str.encode(token)).hexdigest()
|
||||
|
||||
# generate nickname
|
||||
nickname = self.NickGen.short_from_SHA256(hash, max_length=18)[0]
|
||||
context['nickname'] = nickname
|
||||
|
||||
# generate avatar
|
||||
rh = Robohash(hash)
|
||||
rh.assemble(roboset='set1') # bgset='any' for backgrounds ON
|
||||
|
||||
avatars_path = Path('frontend/static/assets/avatars')
|
||||
avatars_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(avatars_path.joinpath(nickname+".png"), "wb") as f:
|
||||
rh.img.save(f, format="png")
|
||||
|
||||
# Create new credentials if nickname is new
|
||||
if len(User.objects.filter(username=nickname)) == 0:
|
||||
User.objects.create_user(username=nickname, password=token, is_staff=False)
|
||||
|
||||
else:
|
||||
context['found'] = 'A matching nickname was found'
|
||||
|
||||
# TODO Keep user authenticated.
|
||||
# BaseBackend.authenticate(self, request=None,username=nickname, password=token)
|
||||
|
||||
return Response(context, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user