253 lines
9.9 KiB
Python
253 lines
9.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from unittest import mock # noqa
|
|
import pytest
|
|
|
|
from awx.api.versioning import reverse
|
|
from awx.main.models import Project
|
|
|
|
from django.core.exceptions import ValidationError
|
|
|
|
|
|
#
|
|
# Project listing and visibility tests
|
|
#
|
|
@pytest.fixture
|
|
def team_project_list(organization_factory):
|
|
objects = organization_factory('org-test',
|
|
superusers=['admin'],
|
|
users=['team1:alice', 'team2:bob'],
|
|
teams=['team1', 'team2'],
|
|
projects=['pteam1', 'pteam2', 'pshared'],
|
|
roles=['team1.member_role:pteam1.admin_role',
|
|
'team2.member_role:pteam2.admin_role',
|
|
'team1.member_role:pshared.admin_role',
|
|
'team2.member_role:pshared.admin_role'])
|
|
return objects
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_get_project_path(project):
|
|
# Test combining projects root with project local path
|
|
with mock.patch('awx.main.models.projects.settings.PROJECTS_ROOT', '/var/lib/foo'):
|
|
assert project.get_project_path(check_if_exists=False) == f'/var/lib/foo/_{project.id}__test_proj'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_project_paged_list(get, organization_factory):
|
|
'Test project listing that spans multiple pages'
|
|
|
|
# 3 total projects, 1 per page, 3 pages
|
|
objects = organization_factory(
|
|
'org1',
|
|
projects=['project-%s' % i for i in range(3)],
|
|
users=['alice'],
|
|
roles=['project-%s.admin_role:alice' % i for i in range(3)],
|
|
)
|
|
|
|
# first page has first project and no previous page
|
|
pk = objects.users.alice.pk
|
|
url = reverse('api:user_projects_list', kwargs={'pk':pk,})
|
|
results = get(url, objects.users.alice, QUERY_STRING='page_size=1').data
|
|
assert results['count'] == 3
|
|
assert len(results['results']) == 1
|
|
assert results['previous'] is None
|
|
assert results['next'] == (
|
|
'/api/v2/users/%s/projects/?page=2&page_size=1' % pk
|
|
)
|
|
|
|
# second page has one more, a previous and next page
|
|
results = get(url, objects.users.alice,
|
|
QUERY_STRING='page=2&page_size=1').data
|
|
assert len(results['results']) == 1
|
|
assert results['previous'] == (
|
|
'/api/v2/users/%s/projects/?page=1&page_size=1' % pk
|
|
)
|
|
assert results['next'] == (
|
|
'/api/v2/users/%s/projects/?page=3&page_size=1' % pk
|
|
)
|
|
|
|
# third page has last project and a previous page
|
|
results = get(url, objects.users.alice,
|
|
QUERY_STRING='page=3&page_size=1').data
|
|
assert len(results['results']) == 1
|
|
assert results['previous'] == (
|
|
'/api/v2/users/%s/projects/?page=2&page_size=1' % pk
|
|
)
|
|
assert results['next'] is None
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_project_paged_list_with_unicode(get, organization_factory):
|
|
'Test project listing that contains unicode chars in the next/prev links'
|
|
|
|
# Create 2 projects that contain a "cloud" unicode character, make sure we
|
|
# can search it and properly generate next/previous page links
|
|
objects = organization_factory(
|
|
'org1',
|
|
projects=['project-☁-1','project-☁-2'],
|
|
users=['alice'],
|
|
roles=['project-☁-1.admin_role:alice','project-☁-2.admin_role:alice'],
|
|
)
|
|
pk = objects.users.alice.pk
|
|
url = reverse('api:user_projects_list', kwargs={'pk':pk,})
|
|
|
|
# first on first page, next page link contains unicode char
|
|
results = get(url, objects.users.alice,
|
|
QUERY_STRING='page_size=1&search=%E2%98%81').data
|
|
assert results['count'] == 2
|
|
assert len(results['results']) == 1
|
|
assert results['next'] == (
|
|
'/api/v2/users/%s/projects/?page=2&page_size=1&search=%%E2%%98%%81' % pk # noqa
|
|
)
|
|
|
|
# second project on second page, previous page link contains unicode char
|
|
results = get(url, objects.users.alice,
|
|
QUERY_STRING='page=2&page_size=1&search=%E2%98%81').data
|
|
assert results['count'] == 2
|
|
assert len(results['results']) == 1
|
|
assert results['previous'] == (
|
|
'/api/v2/users/%s/projects/?page=1&page_size=1&search=%%E2%%98%%81' % pk # noqa
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_project_list(get, organization_factory):
|
|
'List of projects a user has access to, filtered by projects you can also see'
|
|
|
|
objects = organization_factory('org1',
|
|
projects=['alice project', 'bob project', 'shared project'],
|
|
superusers=['admin'],
|
|
users=['alice', 'bob'],
|
|
roles=['alice project.admin_role:alice',
|
|
'bob project.admin_role:bob',
|
|
'shared project.admin_role:bob',
|
|
'shared project.admin_role:alice'])
|
|
assert get(reverse(
|
|
'api:user_projects_list',
|
|
kwargs={'pk':objects.superusers.admin.pk,}
|
|
), objects.superusers.admin).data['count'] == 3
|
|
|
|
# admins can see everyones projects
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':objects.users.alice.pk,}), objects.superusers.admin).data['count'] == 2
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':objects.users.bob.pk,}), objects.superusers.admin).data['count'] == 2
|
|
|
|
# users can see their own projects
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':objects.users.alice.pk,}), objects.users.alice).data['count'] == 2
|
|
|
|
# alice should only be able to see the shared project when looking at bobs projects
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':objects.users.bob.pk,}), objects.users.alice).data['count'] == 1
|
|
|
|
# alice should see all projects they can see when viewing an admin
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':objects.superusers.admin.pk,}), objects.users.alice).data['count'] == 2
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_team_project_list(get, team_project_list):
|
|
objects = team_project_list
|
|
|
|
team1, team2 = objects.teams.team1, objects.teams.team2
|
|
alice, bob, admin = objects.users.alice, objects.users.bob, objects.superusers.admin
|
|
|
|
# admins can see all projects on a team
|
|
assert get(reverse('api:team_projects_list', kwargs={'pk':team1.pk,}), admin).data['count'] == 2
|
|
assert get(reverse('api:team_projects_list', kwargs={'pk':team2.pk,}), admin).data['count'] == 2
|
|
|
|
# users can see all projects on teams they are a member of
|
|
assert get(reverse('api:team_projects_list', kwargs={'pk':team1.pk,}), alice).data['count'] == 2
|
|
|
|
# but if she does, then she should only see the shared project
|
|
team2.read_role.members.add(alice)
|
|
assert get(reverse('api:team_projects_list', kwargs={'pk':team2.pk,}), alice).data['count'] == 1
|
|
team2.read_role.members.remove(alice)
|
|
|
|
# admins can see all projects
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':admin.pk,}), admin).data['count'] == 3
|
|
|
|
# admins can see everyones projects
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':alice.pk,}), admin).data['count'] == 2
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':bob.pk,}), admin).data['count'] == 2
|
|
|
|
# users can see their own projects
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':alice.pk,}), alice).data['count'] == 2
|
|
|
|
# alice should see all projects they can see when viewing an admin
|
|
assert get(reverse('api:user_projects_list', kwargs={'pk':admin.pk,}), alice).data['count'] == 2
|
|
|
|
|
|
@pytest.mark.parametrize("u,expected_status_code", [
|
|
('rando', 403),
|
|
('org_member', 403),
|
|
('org_admin', 201),
|
|
('admin', 201)
|
|
])
|
|
@pytest.mark.django_db()
|
|
def test_create_project(post, organization, org_admin, org_member, admin, rando, u, expected_status_code):
|
|
if u == 'rando':
|
|
u = rando
|
|
elif u == 'org_member':
|
|
u = org_member
|
|
elif u == 'org_admin':
|
|
u = org_admin
|
|
elif u == 'admin':
|
|
u = admin
|
|
|
|
result = post(reverse('api:project_list'), {
|
|
'name': 'Project',
|
|
'organization': organization.id,
|
|
}, u)
|
|
print(result.data)
|
|
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_project_credential_protection(post, put, project, organization, scm_credential, org_admin):
|
|
project.save()
|
|
project.admin_role.members.add(org_admin)
|
|
put(
|
|
reverse('api:project_detail', kwargs={'pk':project.id}), {
|
|
'name': 'should not change',
|
|
'credential': scm_credential.id
|
|
}, org_admin, expect=403
|
|
)
|
|
post(
|
|
reverse('api:project_list'), {
|
|
'name': 'should not create',
|
|
'organization':organization.id,
|
|
'credential': scm_credential.id
|
|
}, org_admin, expect=403
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_cannot_schedule_manual_project(manual_project, admin_user, post):
|
|
response = post(
|
|
reverse('api:project_schedules_list', kwargs={'pk':manual_project.pk,}),
|
|
{
|
|
"name": "foo", "description": "", "enabled": True,
|
|
"rrule": "DTSTART:20160926T040000Z RRULE:FREQ=HOURLY;INTERVAL=1",
|
|
"extra_data": {}
|
|
}, admin_user, expect=400
|
|
)
|
|
assert 'Manual' in response.data['unified_job_template'][0]
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_project_unique_together_with_org(organization):
|
|
proj1 = Project(name='foo', organization=organization)
|
|
proj1.save()
|
|
proj2 = Project(name='foo', organization=organization)
|
|
with pytest.raises(ValidationError):
|
|
proj2.validate_unique()
|
|
proj2 = Project(name='foo', organization=None)
|
|
proj2.validate_unique()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_project_delete(delete, organization, admin_user):
|
|
proj = Project(name='foo', organization=organization)
|
|
proj.save()
|
|
delete(reverse('api:project_detail', kwargs={'pk':proj.id,}), admin_user)
|