mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 12:11:35 +00:00
Delete user view, session auth and png avatars (#588)
This commit is contained in:
parent
13a1fac202
commit
ca3f7cb222
@ -65,7 +65,7 @@ class Robot(models.Model):
|
||||
|
||||
# RoboHash
|
||||
avatar = models.ImageField(
|
||||
default=("static/assets/avatars/" + "unknown_avatar.png"),
|
||||
default=("static/assets/avatars/" + "unknown_avatar.webp"),
|
||||
verbose_name="Avatar",
|
||||
blank=True,
|
||||
)
|
||||
|
@ -356,241 +356,6 @@ class OrderViewSchema:
|
||||
}
|
||||
|
||||
|
||||
class UserViewSchema:
|
||||
post = {
|
||||
"summary": "Create user",
|
||||
"description": textwrap.dedent(
|
||||
"""
|
||||
Create a new Robot 🤖
|
||||
|
||||
`token_sha256` is the SHA256 hash of your token. Make sure you generate your token
|
||||
using cryptographically secure methods. [Here's]() the function the Javascript
|
||||
client uses to generate the tokens. Since the server only recieves the hash of the
|
||||
token, it trusts the client with computing `length`, `counts` and `unique_values`
|
||||
correctly. Check [here](https://github.com/RoboSats/robosats/blob/main/frontend/src/utils/token.js#L13)
|
||||
to see how the Javascript client copmutes these values. These values are optional,
|
||||
but if provided, the api computes the entropy of the token adds two additional
|
||||
fields to the response JSON - `token_shannon_entropy` and `token_bits_entropy`.
|
||||
|
||||
**Note: It is entirely the clients responsibilty to generate high entropy tokens, and the optional
|
||||
parameters are provided to act as an aid to help determine sufficient entropy, but the server is happy
|
||||
with just any sha256 hash you provide it**
|
||||
|
||||
`public_key` - PGP key associated with the user (Armored ASCII format)
|
||||
`encrypted_private_key` - Private PGP key. This is only stored on the backend for later fetching by
|
||||
the frontend and the key can't really be used by the server since it's protected by the token
|
||||
that only the client knows. Will be made an optional parameter in a future release.
|
||||
On the Javascript client, It's passphrase is set to be the secret token generated.
|
||||
|
||||
A gpg key can be created by:
|
||||
|
||||
```shell
|
||||
gpg --full-gen-key
|
||||
```
|
||||
|
||||
it's public key can be exported in ascii armored format with:
|
||||
|
||||
```shell
|
||||
gpg --export --armor <key-id | email | name>
|
||||
```
|
||||
|
||||
and it's private key can be exported in ascii armored format with:
|
||||
|
||||
```shell
|
||||
gpg --export-secret-keys --armor <key-id | email | name>
|
||||
```
|
||||
|
||||
"""
|
||||
),
|
||||
"responses": {
|
||||
201: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"encrypted_private_key": {
|
||||
"type": "string",
|
||||
"description": "Armored ASCII PGP private key block",
|
||||
},
|
||||
"nickname": {
|
||||
"type": "string",
|
||||
"description": "Username generated (Robot name)",
|
||||
},
|
||||
"public_key": {
|
||||
"type": "string",
|
||||
"description": "Armored ASCII PGP public key block",
|
||||
},
|
||||
"token_bits_entropy": {"type": "integer"},
|
||||
"token_shannon_entropy": {"type": "integer"},
|
||||
"wants_stealth": {
|
||||
"type": "boolean",
|
||||
"default": False,
|
||||
"description": "Whether the user prefers stealth invoices",
|
||||
},
|
||||
},
|
||||
},
|
||||
202: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"encrypted_private_key": {
|
||||
"type": "string",
|
||||
"description": "Armored ASCII PGP private key block",
|
||||
},
|
||||
"nickname": {
|
||||
"type": "string",
|
||||
"description": "Username generated (Robot name)",
|
||||
},
|
||||
"public_key": {
|
||||
"type": "string",
|
||||
"description": "Armored ASCII PGP public key block",
|
||||
},
|
||||
"token_bits_entropy": {"type": "integer"},
|
||||
"token_shannon_entropy": {"type": "integer"},
|
||||
"wants_stealth": {
|
||||
"type": "boolean",
|
||||
"default": False,
|
||||
"description": "Whether the user prefers stealth invoices",
|
||||
},
|
||||
"found": {"type": "string", "description": "Welcome back message"},
|
||||
"tg_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The robot has telegram notifications enabled",
|
||||
},
|
||||
"tg_token": {
|
||||
"type": "string",
|
||||
"description": "Token to enable telegram with /start <tg_token>",
|
||||
},
|
||||
"tg_bot_name": {
|
||||
"type": "string",
|
||||
"description": "Name of the coordinator's telegram bot",
|
||||
},
|
||||
"last_login": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Last time seen",
|
||||
},
|
||||
"active_order_id": {
|
||||
"type": "integer",
|
||||
"description": "Active order id if present",
|
||||
},
|
||||
"last_order_id": {
|
||||
"type": "integer",
|
||||
"description": "Last order id if present",
|
||||
},
|
||||
},
|
||||
},
|
||||
400: {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"active_order_id": {
|
||||
"type": "string",
|
||||
"description": "Order id the robot is a maker/taker of",
|
||||
},
|
||||
"nickname": {
|
||||
"type": "string",
|
||||
"description": "Username (Robot name)",
|
||||
},
|
||||
"bad_request": {
|
||||
"type": "string",
|
||||
"description": "Reason for the failure",
|
||||
"default": "You are already logged in as {nickname} and have an active order",
|
||||
},
|
||||
},
|
||||
"description": "Response when you already authenticated and have an order",
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bad_request": {
|
||||
"type": "string",
|
||||
"description": "Reason for the failure",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
403: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bad_request": {
|
||||
"type": "string",
|
||||
"description": "Reason for the failure",
|
||||
"default": "Enter a different token",
|
||||
},
|
||||
"found": {
|
||||
"type": "string",
|
||||
"default": "Bad luck, this nickname is taken",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"examples": [
|
||||
OpenApiExample(
|
||||
"Successfuly created user",
|
||||
value={
|
||||
"token_shannon_entropy": 0.7714559798089662,
|
||||
"token_bits_entropy": 169.21582985307933,
|
||||
"nickname": "StackerMan420",
|
||||
"public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n......\n......",
|
||||
"encrypted_private_key": "-----BEGIN PGP PRIVATE KEY BLOCK-----\n\n......\n......",
|
||||
"wants_stealth": False,
|
||||
},
|
||||
status_codes=[201],
|
||||
),
|
||||
OpenApiExample(
|
||||
"Already authenticated and have an order",
|
||||
value={
|
||||
"active_order_id": "42069",
|
||||
"nickname": "StackerMan210",
|
||||
"bad_request": "You are already logged in as {nickname} and have an active order",
|
||||
},
|
||||
status_codes=[400],
|
||||
),
|
||||
OpenApiExample(
|
||||
"When required token entropy not met",
|
||||
value={"bad_request": "The token does not have enough entropy"},
|
||||
status_codes=[400],
|
||||
),
|
||||
OpenApiExample(
|
||||
"Invalid PGP public key provided",
|
||||
value={"bad_request": "Your PGP public key does not seem valid"},
|
||||
status_codes=[400],
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
delete = {
|
||||
"summary": "Delete user",
|
||||
"description": textwrap.dedent(
|
||||
"""
|
||||
Delete a Robot. Deleting a robot is not allowed if the robot has an active order, has had completed trades or was created more than 30 mins ago.
|
||||
Mainly used on the frontend to "Generate new Robot" without flooding the DB with discarded robots.
|
||||
"""
|
||||
),
|
||||
"responses": {
|
||||
403: {},
|
||||
400: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bad_request": {
|
||||
"type": "string",
|
||||
"description": "Reason for the failure",
|
||||
},
|
||||
},
|
||||
},
|
||||
301: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_deleted": {
|
||||
"type": "string",
|
||||
"default": "User deleted permanently",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class BookViewSchema:
|
||||
get = {
|
||||
"summary": "Get public orders",
|
||||
@ -627,8 +392,6 @@ class RobotViewSchema:
|
||||
"summary": "Get robot info",
|
||||
"description": textwrap.dedent(
|
||||
"""
|
||||
DEPRECATED: Use `/robot` GET.
|
||||
|
||||
Get robot info 🤖
|
||||
|
||||
An authenticated request (has the token's sha256 hash encoded as base 91 in the Authorization header) will be
|
||||
|
@ -527,64 +527,6 @@ class UpdateOrderSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
|
||||
class UserGenSerializer(serializers.Serializer):
|
||||
# Mandatory fields
|
||||
token_sha256 = serializers.CharField(
|
||||
min_length=64,
|
||||
max_length=64,
|
||||
allow_null=False,
|
||||
allow_blank=False,
|
||||
required=True,
|
||||
help_text="SHA256 of user secret",
|
||||
)
|
||||
# Optional fields
|
||||
# (PGP keys are mandatory for new users, but optional for logins)
|
||||
public_key = serializers.CharField(
|
||||
max_length=2000,
|
||||
allow_null=False,
|
||||
allow_blank=False,
|
||||
required=False,
|
||||
help_text="Armored ASCII PGP public key block",
|
||||
)
|
||||
encrypted_private_key = serializers.CharField(
|
||||
max_length=2000,
|
||||
allow_null=False,
|
||||
allow_blank=False,
|
||||
required=False,
|
||||
help_text="Armored ASCII PGP encrypted private key block",
|
||||
)
|
||||
|
||||
ref_code = serializers.CharField(
|
||||
max_length=30,
|
||||
allow_null=True,
|
||||
allow_blank=True,
|
||||
required=False,
|
||||
default=None,
|
||||
help_text="Referal code",
|
||||
)
|
||||
counts = serializers.ListField(
|
||||
child=serializers.IntegerField(),
|
||||
allow_null=True,
|
||||
required=False,
|
||||
default=None,
|
||||
help_text="Counts of the unique characters in the token",
|
||||
)
|
||||
length = serializers.IntegerField(
|
||||
allow_null=True,
|
||||
default=None,
|
||||
required=False,
|
||||
min_value=1,
|
||||
help_text="Length of the token",
|
||||
)
|
||||
unique_values = serializers.IntegerField(
|
||||
allow_null=True,
|
||||
default=None,
|
||||
required=False,
|
||||
min_value=1,
|
||||
help_text="Number of unique values in the token",
|
||||
)
|
||||
|
||||
|
||||
class ClaimRewardSerializer(serializers.Serializer):
|
||||
invoice = serializers.CharField(
|
||||
max_length=2000,
|
||||
|
225
api/views.py
225
api/views.py
@ -1,26 +1,18 @@
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta
|
||||
from math import log2
|
||||
from pathlib import Path
|
||||
|
||||
from decouple import config
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q, Sum
|
||||
from django.utils import timezone
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework.authentication import (
|
||||
SessionAuthentication, # DEPRECATE session authentication
|
||||
)
|
||||
from rest_framework.authentication import TokenAuthentication
|
||||
from rest_framework.generics import CreateAPIView, ListAPIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from robohash import Robohash
|
||||
from scipy.stats import entropy
|
||||
|
||||
from api.logics import Logics
|
||||
from api.models import Currency, LNPayment, MarketTick, OnchainPayment, Order
|
||||
@ -37,7 +29,6 @@ from api.oas_schemas import (
|
||||
RobotViewSchema,
|
||||
StealthViewSchema,
|
||||
TickViewSchema,
|
||||
UserViewSchema,
|
||||
)
|
||||
from api.serializers import (
|
||||
ClaimRewardSerializer,
|
||||
@ -49,7 +40,6 @@ from api.serializers import (
|
||||
StealthSerializer,
|
||||
TickSerializer,
|
||||
UpdateOrderSerializer,
|
||||
UserGenSerializer,
|
||||
)
|
||||
from api.utils import (
|
||||
compute_avg_premium,
|
||||
@ -57,14 +47,11 @@ from api.utils import (
|
||||
get_cln_version,
|
||||
get_lnd_version,
|
||||
get_robosats_commit,
|
||||
validate_pgp_keys,
|
||||
verify_signed_message,
|
||||
)
|
||||
from chat.models import Message
|
||||
from control.models import AccountingDay, BalanceLog
|
||||
|
||||
from .nick_generator.nick_generator import NickGenerator
|
||||
|
||||
EXP_MAKER_BOND_INVOICE = int(config("EXP_MAKER_BOND_INVOICE"))
|
||||
RETRY_TIME = int(config("RETRY_TIME"))
|
||||
PUBLIC_DURATION = 60 * 60 * int(config("DEFAULT_PUBLIC_ORDER_DURATION")) - 1
|
||||
@ -79,7 +66,7 @@ avatar_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
class MakerView(CreateAPIView):
|
||||
serializer_class = MakeOrderSerializer
|
||||
authentication_classes = [TokenAuthentication, SessionAuthentication]
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(**MakerViewSchema.post)
|
||||
@ -190,7 +177,7 @@ class MakerView(CreateAPIView):
|
||||
|
||||
|
||||
class OrderView(viewsets.ViewSet):
|
||||
authentication_classes = [TokenAuthentication, SessionAuthentication]
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = UpdateOrderSerializer
|
||||
lookup_url_kwarg = "order_id"
|
||||
@ -652,7 +639,7 @@ class OrderView(viewsets.ViewSet):
|
||||
|
||||
|
||||
class RobotView(APIView):
|
||||
authentication_classes = [TokenAuthentication, SessionAuthentication]
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(**RobotViewSchema.get)
|
||||
@ -692,208 +679,6 @@ class RobotView(APIView):
|
||||
return Response(context, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class UserView(APIView):
|
||||
"""
|
||||
Deprecated. UserView will be completely replaced by the smaller RobotView in
|
||||
combination with the RobotTokenSHA256 middleware (on-the-fly robot generation)
|
||||
"""
|
||||
|
||||
NickGen = NickGenerator(
|
||||
lang="English", use_adv=False, use_adj=True, use_noun=True, max_num=999
|
||||
)
|
||||
|
||||
serializer_class = UserGenSerializer
|
||||
|
||||
@extend_schema(**UserViewSchema.post)
|
||||
def post(self, request, format=None):
|
||||
"""
|
||||
Get a new user derived from a high entropy token
|
||||
|
||||
- Request has a hash of a high-entropy token
|
||||
- Request includes pubKey and encrypted privKey
|
||||
- Generates new nickname and avatar.
|
||||
- Creates login credentials (new User object)
|
||||
|
||||
Response with Avatar, Nickname, pubKey, privKey.
|
||||
"""
|
||||
context = {}
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
|
||||
# Return bad request if serializer is not valid
|
||||
if not serializer.is_valid():
|
||||
context = {"bad_request": "Invalid serializer"}
|
||||
return Response(context, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# The new way. The token is never sent. Only its SHA256
|
||||
token_sha256 = serializer.data.get("token_sha256")
|
||||
public_key = serializer.data.get("public_key")
|
||||
encrypted_private_key = serializer.data.get("encrypted_private_key")
|
||||
|
||||
# Now the server only receives a hash of the token. So server trusts the client
|
||||
# with computing length, counts and unique_values to confirm the high entropy of the token
|
||||
# In any case, it is up to the client if they want to create a bad high entropy token.
|
||||
|
||||
# Submitting the three params needed to compute token entropy is not mandatory
|
||||
# If not submitted, avatars can be created with garbage entropy token. Frontend will always submit them.
|
||||
try:
|
||||
unique_values = serializer.data.get("unique_values")
|
||||
counts = serializer.data.get("counts")
|
||||
length = serializer.data.get("length")
|
||||
|
||||
shannon_entropy = entropy(counts, base=62)
|
||||
bits_entropy = log2(unique_values**length)
|
||||
|
||||
# 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)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Hash the token_sha256, only 1 iteration. (this is the second SHA256 of the user token, aka RoboSats ID)
|
||||
hash = hashlib.sha256(token_sha256.encode("utf-8")).hexdigest()
|
||||
|
||||
# Generate nickname deterministically
|
||||
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
|
||||
|
||||
# Does not replace image if existing (avoid re-avatar in case of nick collusion)
|
||||
# Deprecate "png" and keep "webp" only after v0.4.4
|
||||
image_path = avatar_path.joinpath(nickname + ".webp")
|
||||
if not image_path.exists():
|
||||
with open(image_path, "wb") as f:
|
||||
rh.img.save(f, format="WEBP", quality=80)
|
||||
|
||||
image_small_path = avatar_path.joinpath(nickname + ".small.webp")
|
||||
with open(image_small_path, "wb") as f:
|
||||
resized_img = rh.img.resize((80, 80))
|
||||
resized_img.save(f, format="WEBP", quality=80)
|
||||
|
||||
png_path = avatar_path.joinpath(nickname + ".png")
|
||||
with open(png_path, "wb") as f:
|
||||
rh.img.save(f, format="png", optimize=True)
|
||||
|
||||
# Create new credentials and login if nickname is new
|
||||
if len(User.objects.filter(username=nickname)) == 0:
|
||||
if not public_key or not encrypted_private_key:
|
||||
context[
|
||||
"bad_request"
|
||||
] = "Must provide valid 'pub' and 'enc_priv' PGP keys"
|
||||
return Response(context, status.HTTP_400_BAD_REQUEST)
|
||||
(
|
||||
valid,
|
||||
bad_keys_context,
|
||||
public_key,
|
||||
encrypted_private_key,
|
||||
) = validate_pgp_keys(public_key, encrypted_private_key)
|
||||
if not valid:
|
||||
return Response(bad_keys_context, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
User.objects.create_user(
|
||||
username=nickname, password=token_sha256, is_staff=False
|
||||
)
|
||||
user = authenticate(request, username=nickname, password=token_sha256)
|
||||
login(request, user)
|
||||
|
||||
user.robot.avatar = "static/assets/avatars/" + nickname + ".webp"
|
||||
|
||||
# Noticed some PGP keys replaced at re-login. Should not happen.
|
||||
# Let's implement this sanity check "If robot has not keys..."
|
||||
if not user.robot.public_key:
|
||||
user.robot.public_key = public_key
|
||||
if not user.robot.encrypted_private_key:
|
||||
user.robot.encrypted_private_key = encrypted_private_key
|
||||
|
||||
user.robot.save()
|
||||
|
||||
context = {**context, **Telegram.get_context(user)}
|
||||
context["public_key"] = user.robot.public_key
|
||||
context["encrypted_private_key"] = user.robot.encrypted_private_key
|
||||
context["wants_stealth"] = user.robot.wants_stealth
|
||||
return Response(context, status=status.HTTP_201_CREATED)
|
||||
|
||||
# log in user and return pub/priv keys if existing
|
||||
else:
|
||||
user = authenticate(request, username=nickname, password=token_sha256)
|
||||
if user is not None:
|
||||
login(request, user)
|
||||
context["public_key"] = user.robot.public_key
|
||||
context["encrypted_private_key"] = user.robot.encrypted_private_key
|
||||
context["earned_rewards"] = user.robot.earned_rewards
|
||||
context["wants_stealth"] = user.robot.wants_stealth
|
||||
|
||||
# Adds/generate telegram token and whether it is enabled
|
||||
context = {**context, **Telegram.get_context(user)}
|
||||
|
||||
# return active order or last made order if any
|
||||
has_no_active_order, _, order = Logics.validate_already_maker_or_taker(
|
||||
request.user
|
||||
)
|
||||
if not has_no_active_order:
|
||||
context["active_order_id"] = order.id
|
||||
else:
|
||||
last_order = Order.objects.filter(
|
||||
Q(maker=request.user) | Q(taker=request.user)
|
||||
).last()
|
||||
if last_order:
|
||||
context["last_order_id"] = last_order.id
|
||||
|
||||
# Sends the welcome back message.
|
||||
context["found"] = "We found your Robot avatar. Welcome back!"
|
||||
return Response(context, status=status.HTTP_202_ACCEPTED)
|
||||
else:
|
||||
# It is unlikely, but maybe the nickname is taken (1 in 20 Billion chance)
|
||||
context["found"] = "Bad luck, this nickname is taken"
|
||||
context["bad_request"] = "Enter a different token"
|
||||
return Response(context, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
@extend_schema(**UserViewSchema.delete)
|
||||
def delete(self, request):
|
||||
"""Pressing "give me another" deletes the logged in user"""
|
||||
user = request.user
|
||||
if not user.is_authenticated:
|
||||
return Response(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# Only delete if user life is shorter than 30 minutes. Helps to avoid deleting users by mistake
|
||||
if user.date_joined < (timezone.now() - timedelta(minutes=30)):
|
||||
return Response(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Check if it is not a maker or taker!
|
||||
not_participant, _, _ = Logics.validate_already_maker_or_taker(user)
|
||||
if not not_participant:
|
||||
return Response(
|
||||
{
|
||||
"bad_request": "Maybe a mistake? User cannot be deleted while he is part of an order"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
# Check if has already a robot with
|
||||
if user.robot.total_contracts > 0:
|
||||
return Response(
|
||||
{
|
||||
"bad_request": "Maybe a mistake? User cannot be deleted as it has completed trades"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
logout(request)
|
||||
user.delete()
|
||||
return Response(
|
||||
{"user_deleted": "User deleted permanently"},
|
||||
status.HTTP_301_MOVED_PERMANENTLY,
|
||||
)
|
||||
|
||||
|
||||
class BookView(ListAPIView):
|
||||
serializer_class = OrderPublicSerializer
|
||||
queryset = Order.objects.filter(status=Order.Status.PUB)
|
||||
@ -1016,7 +801,7 @@ class InfoView(ListAPIView):
|
||||
|
||||
|
||||
class RewardView(CreateAPIView):
|
||||
authentication_classes = [TokenAuthentication, SessionAuthentication]
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
serializer_class = ClaimRewardSerializer
|
||||
@ -1156,7 +941,7 @@ class HistoricalView(ListAPIView):
|
||||
|
||||
|
||||
class StealthView(APIView):
|
||||
authentication_classes = [TokenAuthentication, SessionAuthentication]
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
serializer_class = StealthSerializer
|
||||
|
@ -5,9 +5,6 @@ from channels.layers import get_channel_layer
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils import timezone
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework.authentication import (
|
||||
SessionAuthentication, # DEPRECATE session Authentication
|
||||
)
|
||||
from rest_framework.authentication import TokenAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
@ -20,7 +17,7 @@ from chat.serializers import ChatSerializer, PostMessageSerializer
|
||||
|
||||
class ChatView(viewsets.ViewSet):
|
||||
serializer_class = PostMessageSerializer
|
||||
authentication_classes = [TokenAuthentication, SessionAuthentication]
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
lookup_url_kwarg = ["order_id", "offset"]
|
||||
|
@ -18,7 +18,6 @@ python-decouple==3.8
|
||||
requests==2.31.0
|
||||
ring==0.10.1
|
||||
git+https://github.com/RoboSats/Robohash.git
|
||||
scipy==1.10.1
|
||||
gunicorn==21.2.0
|
||||
psycopg2==2.9.7
|
||||
SQLAlchemy==2.0.16
|
||||
|
@ -5,7 +5,6 @@ from channels.db import database_sync_to_async
|
||||
from channels.middleware import BaseMiddleware
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser, User, update_last_login
|
||||
from django.db import IntegrityError
|
||||
from rest_framework.authtoken.models import Token
|
||||
from rest_framework.exceptions import AuthenticationFailed
|
||||
from robohash import Robohash
|
||||
@ -97,15 +96,7 @@ class RobotTokenSHA256AuthenticationMiddleWare:
|
||||
# Generate nickname deterministically
|
||||
nickname = NickGen.short_from_SHA256(hash, max_length=18)[0]
|
||||
|
||||
# DEPRECATE. Using Try and Except only as a temporary measure.
|
||||
# This will allow existing robots to be added upgraded with a token.key
|
||||
# After v0.5.0, only the following should remain
|
||||
# `user = User.objects.create_user(username=nickname, password=None)`
|
||||
try:
|
||||
user = User.objects.create_user(username=nickname, password=None)
|
||||
except IntegrityError:
|
||||
# UNIQUE constrain failed, user exist. Get it.
|
||||
user = User.objects.get(username=nickname)
|
||||
user = User.objects.create_user(username=nickname, password=None)
|
||||
|
||||
# Django rest_framework authtokens are limited to 40 characters.
|
||||
# We use base91 so we can store the full entropy in the field.
|
||||
|
Loading…
Reference in New Issue
Block a user