Views: move the base64 encoding to the renderers.

Hard-coding the serialization encoding in the serializers is wrong.
This fix now enables us to change to easily change to msgpack as the
transport layer.
This commit is contained in:
Tom Hacohen 2020-06-29 14:50:06 +03:00
parent 2880673e27
commit 3dfceb63b1
3 changed files with 35 additions and 16 deletions

View File

@ -0,0 +1,18 @@
from rest_framework.utils.encoders import JSONEncoder as DRFJSONEncoder
from rest_framework.renderers import JSONRenderer as DRFJSONRenderer
from .serializers import b64encode
class JSONEncoder(DRFJSONEncoder):
def default(self, obj):
if isinstance(obj, bytes) or isinstance(obj, memoryview):
return b64encode(obj)
return super().default(obj)
class JSONRenderer(DRFJSONRenderer):
"""
Renderer which serializes to JSON with support for our base64
"""
encoder_class = JSONEncoder

View File

@ -56,20 +56,21 @@ def b64decode(data):
return base64.urlsafe_b64decode(data) return base64.urlsafe_b64decode(data)
class BinaryBase64Field(serializers.Field): def b64decode_or_bytes(data):
def to_representation(self, value):
if self.context.get('supports_binary', False):
return value
else:
return b64encode(value)
def to_internal_value(self, data):
if isinstance(data, bytes): if isinstance(data, bytes):
return data return data
else: else:
return b64decode(data) return b64decode(data)
class BinaryBase64Field(serializers.Field):
def to_representation(self, value):
return value
def to_internal_value(self, data):
return b64decode_or_bytes(data)
class CollectionEncryptionKeyField(BinaryBase64Field): class CollectionEncryptionKeyField(BinaryBase64Field):
def get_attribute(self, instance): def get_attribute(self, instance):
request = self.context.get('request', None) request = self.context.get('request', None)
@ -91,14 +92,14 @@ class ChunksField(serializers.RelatedField):
obj = obj.chunk obj = obj.chunk
if self.context.get('prefetch'): if self.context.get('prefetch'):
with open(obj.chunkFile.path, 'rb') as f: with open(obj.chunkFile.path, 'rb') as f:
return (obj.uid, b64encode(f.read())) return (obj.uid, f.read())
else: else:
return (obj.uid, ) return (obj.uid, )
def to_internal_value(self, data): def to_internal_value(self, data):
if data[0] is None or data[1] is None: if data[0] is None or data[1] is None:
raise serializers.ValidationError('null is not allowed') raise serializers.ValidationError('null is not allowed')
return (data[0], b64decode(data[1])) return (data[0], b64decode_or_bytes(data[1]))
class CollectionItemChunkSerializer(serializers.ModelSerializer): class CollectionItemChunkSerializer(serializers.ModelSerializer):

View File

@ -29,7 +29,7 @@ from rest_framework import viewsets
from rest_framework.decorators import action as action_decorator from rest_framework.decorators import action as action_decorator
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer from rest_framework.renderers import BrowsableAPIRenderer
import nacl.encoding import nacl.encoding
import nacl.signing import nacl.signing
@ -42,6 +42,7 @@ from .drf_msgpack.parsers import MessagePackParser
from .drf_msgpack.renderers import MessagePackRenderer from .drf_msgpack.renderers import MessagePackRenderer
from . import app_settings, permissions from . import app_settings, permissions
from .renderers import JSONRenderer
from .models import ( from .models import (
Collection, Collection,
CollectionItem, CollectionItem,
@ -53,7 +54,6 @@ from .models import (
UserInfo, UserInfo,
) )
from .serializers import ( from .serializers import (
b64encode,
AuthenticationChangePasswordInnerSerializer, AuthenticationChangePasswordInnerSerializer,
AuthenticationSignupSerializer, AuthenticationSignupSerializer,
AuthenticationLoginChallengeSerializer, AuthenticationLoginChallengeSerializer,
@ -700,8 +700,8 @@ class AuthenticationViewSet(viewsets.ViewSet):
challenge = box.encrypt(msgpack_encode(challenge_data), encoder=nacl.encoding.RawEncoder) challenge = box.encrypt(msgpack_encode(challenge_data), encoder=nacl.encoding.RawEncoder)
ret = { ret = {
"salt": b64encode(salt), "salt": salt,
"challenge": b64encode(challenge), "challenge": challenge,
"version": user.userinfo.version, "version": user.userinfo.version,
} }
return Response(ret, status=status.HTTP_200_OK) return Response(ret, status=status.HTTP_200_OK)
@ -717,7 +717,7 @@ class AuthenticationViewSet(viewsets.ViewSet):
response = msgpack_decode(response_raw) response = msgpack_decode(response_raw)
signature = outer_serializer.validated_data['signature'] signature = outer_serializer.validated_data['signature']
context = {'host': request.get_host(), 'supports_binary': True} context = {'host': request.get_host()}
serializer = AuthenticationLoginInnerSerializer(data=response, context=context) serializer = AuthenticationLoginInnerSerializer(data=response, context=context)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
@ -750,7 +750,7 @@ class AuthenticationViewSet(viewsets.ViewSet):
response = msgpack_decode(response_raw) response = msgpack_decode(response_raw)
signature = outer_serializer.validated_data['signature'] signature = outer_serializer.validated_data['signature']
context = {'host': request.get_host(), 'supports_binary': True} context = {'host': request.get_host()}
serializer = AuthenticationChangePasswordInnerSerializer(request.user.userinfo, data=response, context=context) serializer = AuthenticationChangePasswordInnerSerializer(request.user.userinfo, data=response, context=context)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)