diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 2f58b776f4..1487fe17e3 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -918,6 +918,19 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer): args=(obj.last_update.pk,)) return res + def validate(self, attrs): + organization = None + if 'organization' in attrs: + organization = attrs['organization'] + elif self.instance: + organization = self.instance.organization + + view = self.context.get('view', None) + if not organization and not view.request.user.is_superuser: + # Only allow super users to create orgless projects + raise serializers.ValidationError('Organization is missing') + return super(ProjectSerializer, self).validate(attrs) + class ProjectPlaybooksSerializer(ProjectSerializer): diff --git a/awx/main/tests/functional/test_projects.py b/awx/main/tests/functional/test_projects.py index 4c32d1dd69..40ea659432 100644 --- a/awx/main/tests/functional/test_projects.py +++ b/awx/main/tests/functional/test_projects.py @@ -114,3 +114,19 @@ def test_create_project(post, organization, org_admin, org_member, admin, rando, assert result.status_code == expected_status_code if expected_status_code == 201: assert Project.objects.filter(name='Project', organization=organization).exists() + +@pytest.mark.django_db() +def test_create_project_null_organization(post, organization, admin): + post(reverse('api:project_list'), { 'name': 't', 'organization': None}, admin, expect=201) + +@pytest.mark.django_db() +def test_create_project_null_organization_xfail(post, organization, org_admin): + post(reverse('api:project_list'), { 'name': 't', 'organization': None}, org_admin, expect=400) + +@pytest.mark.django_db() +def test_patch_project_null_organization(patch, organization, project, admin): + patch(reverse('api:project_detail', args=(project.id,)), { 'name': 't', 'organization': organization.id}, admin, expect=200) + +@pytest.mark.django_db() +def test_patch_project_null_organization_xfail(patch, project, org_admin): + patch(reverse('api:project_detail', args=(project.id,)), { 'name': 't', 'organization': None}, org_admin, expect=400)