diff --git a/awx/api/serializers.py b/awx/api/serializers.py index bd0d3d1ea3..4a674a3a6d 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -915,7 +915,7 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer): class Meta: model = Project - fields = ('*', 'scm_delete_on_next_update', 'scm_update_on_launch', + fields = ('*', 'organization', 'scm_delete_on_next_update', 'scm_update_on_launch', 'scm_update_cache_timeout') + \ ('last_update_failed', 'last_updated') # Backwards compatibility read_only_fields = ('scm_delete_on_next_update',) diff --git a/awx/api/views.py b/awx/api/views.py index a93cc40cc8..474e9c6fe8 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -669,6 +669,7 @@ class OrganizationProjectsList(SubListCreateAPIView): serializer_class = ProjectSerializer parent_model = Organization relationship = 'projects' + parent_key = 'organization' class OrganizationTeamsList(SubListCreateAttachDetachAPIView): diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py index 7d45c51fad..b36d0673e0 100644 --- a/awx/main/tests/functional/conftest.py +++ b/awx/main/tests/functional/conftest.py @@ -167,6 +167,24 @@ def alice(user): def bob(user): return user('bob', False) +@pytest.fixture +def rando(user): + "Rando, the random user that doesn't have access to anything" + return user('rando', False) + +@pytest.fixture +def org_admin(user, organization): + ret = user('org-admin', False) + organization.admin_role.members.add(ret) + organization.member_role.members.add(ret) + return ret + +@pytest.fixture +def org_member(user, organization): + ret = user('org-member', False) + organization.member_role.members.add(ret) + return ret + @pytest.fixture def organizations(instance): def rf(organization_count=1): diff --git a/awx/main/tests/functional/test_projects.py b/awx/main/tests/functional/test_projects.py index 13f2a23129..07917acde7 100644 --- a/awx/main/tests/functional/test_projects.py +++ b/awx/main/tests/functional/test_projects.py @@ -2,6 +2,7 @@ import mock # noqa import pytest from django.core.urlresolvers import reverse +from awx.main.models import Project @@ -95,3 +96,38 @@ def test_team_project_list(get, project_factory, team_factory, admin, alice, bob # alice should see all projects they can see when viewing an admin assert get(reverse('api:user_projects_list', args=(admin.pk,)), alice).data['count'] == 2 + + +@pytest.mark.django_db +def test_create_project(post, organization, org_admin, org_member, admin, rando): + test_list = [rando, org_member, org_admin, admin] + expected_status_codes = [403, 403, 201, 201] + + for i, u in enumerate(test_list): + result = post(reverse('api:project_list'), { + 'name': 'Project %d' % i, + 'organization': organization.id, + }, u) + assert result.status_code == expected_status_codes[i] + if expected_status_codes[i] == 201: + assert Project.objects.filter(name='Project %d' % i, organization=organization).exists() + else: + assert not Project.objects.filter(name='Project %d' % i, organization=organization).exists() + +@pytest.mark.django_db +def test_create_project_through_org_link(post, organization, org_admin, org_member, admin, rando): + test_list = [rando, org_member, org_admin, admin] + expected_status_codes = [403, 403, 201, 201] + + for i, u in enumerate(test_list): + result = post(reverse('api:organization_projects_list', args=(organization.id,)), { + 'name': 'Project %d' % i, + }, u) + assert result.status_code == expected_status_codes[i] + if expected_status_codes[i] == 201: + prj = Project.objects.get(name='Project %d' % i) + print(prj.organization) + Project.objects.get(name='Project %d' % i, organization=organization) + assert Project.objects.filter(name='Project %d' % i, organization=organization).exists() + else: + assert not Project.objects.filter(name='Project %d' % i, organization=organization).exists()