mirror of
https://github.com/etesync/server
synced 2024-11-25 18:28:46 +00:00
Incoming invitations: implement incoming invitations and accepting them
This commit is contained in:
parent
8d1c02dcb9
commit
47e1eec122
@ -150,6 +150,7 @@ class CollectionInvitation(models.Model):
|
|||||||
version = models.PositiveSmallIntegerField(default=1)
|
version = models.PositiveSmallIntegerField(default=1)
|
||||||
fromMember = models.ForeignKey(CollectionMember, on_delete=models.CASCADE)
|
fromMember = models.ForeignKey(CollectionMember, on_delete=models.CASCADE)
|
||||||
# FIXME: make sure to delete all invitations for the same collection once one is accepted
|
# FIXME: make sure to delete all invitations for the same collection once one is accepted
|
||||||
|
# Make sure to not allow invitations if already a member
|
||||||
|
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='incoming_invitations', on_delete=models.CASCADE)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='incoming_invitations', on_delete=models.CASCADE)
|
||||||
signedEncryptionKey = models.BinaryField(editable=False, blank=False, null=False)
|
signedEncryptionKey = models.BinaryField(editable=False, blank=False, null=False)
|
||||||
@ -165,6 +166,10 @@ class CollectionInvitation(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{} {}'.format(self.fromMember.collection.uid, self.user)
|
return '{} {}'.format(self.fromMember.collection.uid, self.user)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def collection(self):
|
||||||
|
return self.fromMember.collection
|
||||||
|
|
||||||
|
|
||||||
class UserInfo(models.Model):
|
class UserInfo(models.Model):
|
||||||
owner = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
|
owner = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
|
||||||
|
@ -268,18 +268,20 @@ class CollectionInvitationSerializer(serializers.ModelSerializer):
|
|||||||
slug_field=User.USERNAME_FIELD,
|
slug_field=User.USERNAME_FIELD,
|
||||||
queryset=User.objects
|
queryset=User.objects
|
||||||
)
|
)
|
||||||
collection = serializers.SlugRelatedField(
|
collection = serializers.SerializerMethodField('get_collection')
|
||||||
source='fromMember__collection',
|
fromPubkey = serializers.SerializerMethodField('get_from_pubkey')
|
||||||
slug_field='uid',
|
|
||||||
read_only=True,
|
|
||||||
)
|
|
||||||
fromPubkey = BinaryBase64Field(source='fromMember__user__userinfo__pubkey', read_only=True)
|
|
||||||
signedEncryptionKey = BinaryBase64Field()
|
signedEncryptionKey = BinaryBase64Field()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.CollectionInvitation
|
model = models.CollectionInvitation
|
||||||
fields = ('username', 'uid', 'collection', 'signedEncryptionKey', 'accessLevel', 'fromPubkey', 'version')
|
fields = ('username', 'uid', 'collection', 'signedEncryptionKey', 'accessLevel', 'fromPubkey', 'version')
|
||||||
|
|
||||||
|
def get_collection(self, obj):
|
||||||
|
return obj.collection.uid
|
||||||
|
|
||||||
|
def get_from_pubkey(self, obj):
|
||||||
|
return b64encode(obj.fromMember.user.userinfo.pubkey)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
collection = self.context['collection']
|
collection = self.context['collection']
|
||||||
request = self.context['request']
|
request = self.context['request']
|
||||||
@ -301,6 +303,30 @@ class CollectionInvitationSerializer(serializers.ModelSerializer):
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class InvitationAcceptSerializer(serializers.Serializer):
|
||||||
|
encryptionKey = BinaryBase64Field()
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
invitation = self.context['invitation']
|
||||||
|
encryption_key = validated_data.get('encryptionKey')
|
||||||
|
|
||||||
|
member = models.CollectionMember.objects.create(
|
||||||
|
collection=invitation.collection,
|
||||||
|
user=invitation.user,
|
||||||
|
accessLevel=invitation.accessLevel,
|
||||||
|
encryptionKey=encryption_key,
|
||||||
|
)
|
||||||
|
|
||||||
|
invitation.delete()
|
||||||
|
|
||||||
|
return member
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
@ -49,6 +49,7 @@ from .serializers import (
|
|||||||
CollectionItemChunkSerializer,
|
CollectionItemChunkSerializer,
|
||||||
CollectionMemberSerializer,
|
CollectionMemberSerializer,
|
||||||
CollectionInvitationSerializer,
|
CollectionInvitationSerializer,
|
||||||
|
InvitationAcceptSerializer,
|
||||||
UserSerializer,
|
UserSerializer,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -456,6 +457,31 @@ class CollectionInvitationViewSet(BaseViewSet):
|
|||||||
return queryset.filter(fromMember__collection=collection)
|
return queryset.filter(fromMember__collection=collection)
|
||||||
|
|
||||||
|
|
||||||
|
class InvitationIncomingViewSet(BaseViewSet):
|
||||||
|
allowed_methods = ['GET', 'DELETE']
|
||||||
|
queryset = CollectionInvitation.objects.all()
|
||||||
|
serializer_class = CollectionInvitationSerializer
|
||||||
|
lookup_field = 'uid'
|
||||||
|
lookup_url_kwarg = 'invitation_uid'
|
||||||
|
|
||||||
|
def get_queryset(self, queryset=None):
|
||||||
|
if queryset is None:
|
||||||
|
queryset = type(self).queryset
|
||||||
|
|
||||||
|
return queryset.filter(user=self.request.user)
|
||||||
|
|
||||||
|
@action_decorator(detail=True, allowed_methods=['POST'], methods=['POST'])
|
||||||
|
def accept(self, request, invitation_uid=None):
|
||||||
|
invitation = get_object_or_404(self.get_queryset(), uid=invitation_uid)
|
||||||
|
context = self.get_serializer_context()
|
||||||
|
context.update({'invitation': invitation})
|
||||||
|
|
||||||
|
serializer = InvitationAcceptSerializer(data=request.data, context=context)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
return Response(status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationViewSet(viewsets.ViewSet):
|
class AuthenticationViewSet(viewsets.ViewSet):
|
||||||
allowed_methods = ['POST']
|
allowed_methods = ['POST']
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user