import base64 from django.contrib.auth import get_user_model from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from .models import * class VPNPoolSerializer(serializers.ModelSerializer): class Meta: model = VPNPool fields = '__all__' class VPNNetworkReservationSerializer(serializers.ModelSerializer): class Meta: model = VPNNetworkReservation fields = '__all__' class VPNNetworkSerializer(serializers.ModelSerializer): class Meta: model = VPNNetwork fields = '__all__' # This is required for finding the VPN pool, but does not # exist in the model network_size = serializers.IntegerField(min_value=0, max_value=128, write_only=True) def validate_wireguard_public_key(self, value): msg = _("Supplied key is not a valid wireguard public key") """ FIXME: verify that this does not create broken wireguard config files, i.e. contains \n or similar! We might even need to be more strict to not break wireguard... """ try: base64.standard_b64decode(value) except Exception as e: raise serializers.ValidationError(msg) if '\n' in value: raise serializers.ValidationError(msg) return value def validate(self, data): # FIXME: filter for status = active or similar all_pools = VPNPool.objects.all() sizes = [ p.subnetwork_size for p in all_pools ] pools = VPNPool.objects.filter(subnetwork_size=data['network_size']) if len(pools) == 0: msg = _("No pool available for networks with size = {}. Available are: {}".format(data['network_size'], sizes)) raise serializers.ValidationError(msg) return data def create(self, validated_data): """ Creating a new vpnnetwork - there are a couple of race conditions, especially when run in parallel. What we should be doing: - create a reservation race free - map the reservation to a network (?) """ pools = VPNPool.objects.filter(subnetwork_size=validated_data['network_size']) vpn_network = None for pool in pools: if pool.num_free_networks > 0: next_address = pool.next_free_network reservation, created = VPNNetworkReservation.objects.update_or_create( vpnpool=pool, address=next_address, defaults = { 'status': 'used' }) vpn_network = VPNNetwork.objects.create( owner=self.context['request'].user, network=reservation, wireguard_public_key=validated_data['wireguard_public_key'] ) break if not vpn_network: # FIXME: use correct exception raise Exception("Did not find any free pool") return vpn_network