From 7d7503279de838247a80aadc2a78e2f9ac595158 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Thu, 9 Nov 2023 14:12:45 -0500 Subject: [PATCH] Add API validation when creating ReceptorAddress - websocket_path can only be set if protocol is ws - is_internal must be False - only 1 address per instance can have peers_from_control_nodes set to True Signed-off-by: Seth Foster --- awx/api/serializers.py | 54 +++++++++++-------- awx/main/migrations/0188_inbound_hop_nodes.py | 10 +++- awx/main/models/receptor_address.py | 3 +- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index b2beb2b6b8..34abf4ac42 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -5491,11 +5491,34 @@ class ReceptorAddressSerializer(BaseSerializer): class Meta: model = ReceptorAddress fields = ('id', 'url', 'address', 'port', 'protocol', 'websocket_path', 'is_internal', 'instance', 'peers_from_control_nodes', 'full_address') - read_only = 'full_address' + read_only_fields = ('full_address',) def get_full_address(self, obj): return obj.get_full_address() + def validate(self, attrs): + def get_field_from_model_or_attrs(fd): + return attrs.get(fd, self.instance and getattr(self.instance, fd) or None) + + peers_from_control_nodes = get_field_from_model_or_attrs('peers_from_control_nodes') + instance = get_field_from_model_or_attrs('instance') + + # only allow websocket_path to be set if protocol is ws + if attrs.get('protocol') != 'ws' and attrs.get('websocket_path'): + raise serializers.ValidationError(_("Can only set websocket path if protocol is ws")) + + # an instance can only have one address with peers_from_control_nodes set to True + if peers_from_control_nodes: + for other_address in ReceptorAddress.objects.filter(instance=instance.id): + if other_address.peers_from_control_nodes: + raise serializers.ValidationError(_("Only one address can set peers_from_control_nodes to True")) + + # is_internal should be False + if attrs.get('is_internal') == True: + raise serializers.ValidationError(_("Only external addresses can be created")) + + return super().validate(attrs) + class InstanceSerializer(BaseSerializer): show_capabilities = ['edit'] @@ -5603,7 +5626,7 @@ class InstanceSerializer(BaseSerializer): ''' return True if - 'peers' in attrs - - instance peers matches peers in attrs + - instance peers does not match peers in attrs ''' return self.instance and 'peers' in attrs and set(self.instance.peers.all()) != set(attrs['peers']) @@ -5611,27 +5634,16 @@ class InstanceSerializer(BaseSerializer): raise serializers.ValidationError(_("Can only create instances on Kubernetes or OpenShift.")) node_type = get_field_from_model_or_attrs("node_type") - # peers = attrs.get('peers', []) - # if node_type in [Instance.Types.CONTROL, Instance.Types.HYBRID]: - # if check_peers_changed(): - # raise serializers.ValidationError( - # _("Setting peers manually for control nodes is not allowed. Enable peers_from_control_nodes on the hop and execution nodes instead.") - # ) + if node_type in [Instance.Types.CONTROL, Instance.Types.HYBRID]: + if check_peers_changed(): + raise serializers.ValidationError( + _("Setting peers manually for control nodes is not allowed. Enable peers_from_control_nodes on the hop and execution nodes instead.") + ) - # if not listener_port and peers_from_control_nodes: - # raise serializers.ValidationError(_("Field listener_port must be a valid integer when peers_from_control_nodes is enabled.")) - - # if not listener_port and self.instance and self.instance.peers_from.exists(): - # raise serializers.ValidationError(_("Field listener_port must be a valid integer when other nodes peer to it.")) - - # for peer in peers: - # if peer.listener_port is None: - # raise serializers.ValidationError(_("Field listener_port must be set on peer ") + peer.hostname + ".") - - # if not settings.IS_K8S: - # if check_peers_changed(): - # raise serializers.ValidationError(_("Cannot change peers.")) + if not settings.IS_K8S: + if check_peers_changed(): + raise serializers.ValidationError(_("Cannot change peers.")) return super().validate(attrs) diff --git a/awx/main/migrations/0188_inbound_hop_nodes.py b/awx/main/migrations/0188_inbound_hop_nodes.py index 117012c4d7..963eb10e19 100644 --- a/awx/main/migrations/0188_inbound_hop_nodes.py +++ b/awx/main/migrations/0188_inbound_hop_nodes.py @@ -1,5 +1,6 @@ -# Generated by Django 4.2.6 on 2023-11-02 18:07 +# Generated by Django 4.2.6 on 2023-11-09 19:11 +import django.core.validators from django.db import migrations, models import django.db.models.deletion @@ -15,7 +16,12 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('address', models.CharField(max_length=255)), - ('port', models.IntegerField(default=27199)), + ( + 'port', + models.IntegerField( + default=27199, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)] + ), + ), ('protocol', models.CharField(default='tcp', max_length=10)), ('websocket_path', models.CharField(blank=True, default='', max_length=255)), ('is_internal', models.BooleanField(default=False)), diff --git a/awx/main/models/receptor_address.py b/awx/main/models/receptor_address.py index 0de057f4c9..9599d0e9d5 100644 --- a/awx/main/models/receptor_address.py +++ b/awx/main/models/receptor_address.py @@ -1,4 +1,5 @@ from django.db import models +from django.core.validators import MinValueValidator, MaxValueValidator from django.utils.translation import gettext_lazy as _ from django.db.models.signals import post_save, post_delete from django.dispatch import receiver @@ -18,7 +19,7 @@ class ReceptorAddress(models.Model): ] address = models.CharField(max_length=255) - port = models.IntegerField(default=27199) + port = models.IntegerField(default=27199, validators=[MinValueValidator(0), MaxValueValidator(65535)]) protocol = models.CharField(max_length=10, default="tcp") websocket_path = models.CharField(max_length=255, default="", blank=True) is_internal = models.BooleanField(default=False)