diff --git a/awx/api/views/webhooks.py b/awx/api/views/webhooks.py index 25fb4b56bb..247e13fe54 100644 --- a/awx/api/views/webhooks.py +++ b/awx/api/views/webhooks.py @@ -38,6 +38,7 @@ class WebhookKeyView(GenericAPIView): def post(self, request, *args, **kwargs): obj = self.get_object() obj.rotate_webhook_key() + obj.save(update_fields=['webhook_key']) return Response({'webhook_key': obj.webhook_key}, status=status.HTTP_201_CREATED) diff --git a/awx/main/models/mixins.py b/awx/main/models/mixins.py index c0e37faec4..2ab8fc786e 100644 --- a/awx/main/models/mixins.py +++ b/awx/main/models/mixins.py @@ -515,4 +515,17 @@ class WebhookMixin(models.Model): def rotate_webhook_key(self): self.webhook_key = get_random_string(length=50) - self.save(update_fields=['webhook_key']) + + def save(self, *args, **kwargs): + update_fields = kwargs.get('update_fields') + + if not self.pk or self._values_have_edits({'webhook_service': self.webhook_service}): + if self.webhook_service: + self.rotate_webhook_key() + else: + self.webhook_key = '' + + if update_fields and 'webhook_service' in update_fields: + update_fields.append('webhook_key') + + super().save(*args, **kwargs) diff --git a/awx/main/tests/factories/fixtures.py b/awx/main/tests/factories/fixtures.py index fe61410908..2f8cbe6934 100644 --- a/awx/main/tests/factories/fixtures.py +++ b/awx/main/tests/factories/fixtures.py @@ -154,12 +154,12 @@ def mk_job_template(name, job_type='run', organization=None, inventory=None, credential=None, network_credential=None, cloud_credential=None, persisted=True, extra_vars='', - project=None, spec=None): + project=None, spec=None, webhook_service=''): if extra_vars: extra_vars = json.dumps(extra_vars) jt = JobTemplate(name=name, job_type=job_type, extra_vars=extra_vars, - playbook='helloworld.yml') + webhook_service=webhook_service, playbook='helloworld.yml') jt.inventory = inventory if jt.inventory is None: @@ -200,11 +200,13 @@ def mk_workflow_job(status='new', workflow_job_template=None, extra_vars={}, return job -def mk_workflow_job_template(name, extra_vars='', spec=None, organization=None, persisted=True): +def mk_workflow_job_template(name, extra_vars='', spec=None, organization=None, persisted=True, + webhook_service=''): if extra_vars: extra_vars = json.dumps(extra_vars) - wfjt = WorkflowJobTemplate(name=name, extra_vars=extra_vars, organization=organization) + wfjt = WorkflowJobTemplate(name=name, extra_vars=extra_vars, organization=organization, + webhook_service=webhook_service) wfjt.survey_spec = spec if wfjt.survey_spec: diff --git a/awx/main/tests/factories/tower.py b/awx/main/tests/factories/tower.py index e8b0cc6e42..bfa7f9fc1b 100644 --- a/awx/main/tests/factories/tower.py +++ b/awx/main/tests/factories/tower.py @@ -197,7 +197,7 @@ def create_survey_spec(variables=None, default_type='integer', required=True, mi # -def create_job_template(name, roles=None, persisted=True, **kwargs): +def create_job_template(name, roles=None, persisted=True, webhook_service='', **kwargs): Objects = generate_objects(["job_template", "jobs", "organization", "inventory", @@ -252,11 +252,10 @@ def create_job_template(name, roles=None, persisted=True, **kwargs): else: spec = None - jt = mk_job_template(name, project=proj, - inventory=inv, credential=cred, + jt = mk_job_template(name, project=proj, inventory=inv, credential=cred, network_credential=net_cred, cloud_credential=cloud_cred, job_type=job_type, spec=spec, extra_vars=extra_vars, - persisted=persisted) + persisted=persisted, webhook_service=webhook_service) if 'jobs' in kwargs: for i in kwargs['jobs']: @@ -401,7 +400,7 @@ def generate_workflow_job_template_nodes(workflow_job_template, # TODO: Implement survey and jobs -def create_workflow_job_template(name, organization=None, persisted=True, **kwargs): +def create_workflow_job_template(name, organization=None, persisted=True, webhook_service='', **kwargs): Objects = generate_objects(["workflow_job_template", "workflow_job_template_nodes", "survey",], kwargs) @@ -418,7 +417,8 @@ def create_workflow_job_template(name, organization=None, persisted=True, **kwar organization=organization, spec=spec, extra_vars=extra_vars, - persisted=persisted) + persisted=persisted, + webhook_service=webhook_service) diff --git a/awx/main/tests/functional/api/test_webhooks.py b/awx/main/tests/functional/api/test_webhooks.py index 183ab13793..3e48906fc7 100644 --- a/awx/main/tests/functional/api/test_webhooks.py +++ b/awx/main/tests/functional/api/test_webhooks.py @@ -1,6 +1,7 @@ import pytest from awx.api.versioning import reverse +from awx.main.models.mixins import WebhookMixin @pytest.mark.django_db @@ -107,3 +108,41 @@ def test_post_webhook_key_wfjt(organization_factory, workflow_job_template_facto response = post(url, {}, user=user, expect=expect) if expect < 400: assert bool(response.data.get('webhook_key')) + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "service", [s for s, _ in WebhookMixin.SERVICES] +) +def test_set_webhook_service(organization_factory, job_template_factory, patch, service): + objs = organization_factory("org", superusers=['admin']) + jt = job_template_factory("jt", organization=objs.organization, + inventory='test_inv', project='test_proj').job_template + admin = objs.superusers.admin + assert (jt.webhook_service, jt.webhook_key) == ('', '') + + url = reverse('api:job_template_detail', kwargs={'pk': jt.pk}) + patch(url, {'webhook_service': service}, user=admin, expect=200) + jt.refresh_from_db() + + assert jt.webhook_service == service + assert jt.webhook_key != '' + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "service", [s for s, _ in WebhookMixin.SERVICES] +) +def test_unset_webhook_service(organization_factory, job_template_factory, patch, service): + objs = organization_factory("org", superusers=['admin']) + jt = job_template_factory("jt", organization=objs.organization, webhook_service=service, + inventory='test_inv', project='test_proj').job_template + admin = objs.superusers.admin + assert jt.webhook_service == service + assert jt.webhook_key != '' + + url = reverse('api:job_template_detail', kwargs={'pk': jt.pk}) + patch(url, {'webhook_service': ''}, user=admin, expect=200) + jt.refresh_from_db() + + assert (jt.webhook_service, jt.webhook_key) == ('', '')