From 0be8be6954a83ad01b80c8f9822ae82d6f51f2ee Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Mon, 4 Apr 2016 09:56:36 -0400 Subject: [PATCH 1/2] rbac active removal test cases fixes --- awx/main/tests/unit/api/test_serializers.py | 23 ++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/awx/main/tests/unit/api/test_serializers.py b/awx/main/tests/unit/api/test_serializers.py index 3cac6a34d8..50e6322d1d 100644 --- a/awx/main/tests/unit/api/test_serializers.py +++ b/awx/main/tests/unit/api/test_serializers.py @@ -76,14 +76,14 @@ class TestJobTemplateSerializerGetRelated(GetRelatedMixin): class TestJobTemplateSerializerGetSummaryFields(GetSummaryFieldsMixin): def test__recent_jobs(self, mocker, job_template, jobs): - job_template.jobs.filter = mocker.MagicMock(**{'order_by.return_value': jobs}) - job_template.jobs.filter.return_value = job_template.jobs.filter + job_template.jobs.all = mocker.MagicMock(**{'order_by.return_value': jobs}) + job_template.jobs.all.return_value = job_template.jobs.all serializer = JobTemplateSerializer() recent_jobs = serializer._recent_jobs(job_template) - job_template.jobs.filter.assert_called_with(active=True) - job_template.jobs.filter.order_by.assert_called_with('-created') + job_template.jobs.all.assert_called_once_with() + job_template.jobs.all.order_by.assert_called_once_with('-created') assert len(recent_jobs) == 10 for x in jobs[:10]: assert recent_jobs == [{'id': x.id, 'status': x.status, 'finished': x.finished} for x in jobs[:10]] @@ -126,18 +126,17 @@ class TestJobSerializerGetRelated(GetRelatedMixin): def test_get_related(self, mocker, job, related_resource_name): self._test_get_related(JobSerializer, job, 'jobs', related_resource_name) - def test_job_template_present(self, job): - job.job_template.active = True - serializer = JobSerializer() - related = serializer.get_related(job) - assert 'job_template' in related - - def test_job_template_absent(self, job): - job.job_template.active = False + def test_job_template_absent(self, mocker, job): + job.job_template = None serializer = JobSerializer() related = serializer.get_related(job) assert 'job_template' not in related + def test_job_template_present(self, job): + related = self._mock_and_run(JobSerializer, job) + assert 'job_template' in related + assert related['job_template'] == '/api/v1/%s/%d/' % ('job_templates', job.job_template.pk) + @mock.patch('awx.api.serializers.BaseSerializer.get_summary_fields', lambda x,y: {}) class TestJobOptionsSerializerGetSummaryFields(GetSummaryFieldsMixin): def test__summary_field_labels_10_max(self, mocker, job_template, labels): From fa6f5329cf30e9bb9aa75b748e8a1deb0d894113 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Mon, 4 Apr 2016 12:55:58 -0400 Subject: [PATCH 2/2] labels + rbac + tests --- awx/main/access.py | 32 +++++++++++-- awx/main/tests/functional/conftest.py | 7 +-- awx/main/tests/functional/test_rbac_label.py | 50 ++++++++++++++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 awx/main/tests/functional/test_rbac_label.py diff --git a/awx/main/access.py b/awx/main/access.py index fc89a3487f..510bc814d1 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -881,7 +881,7 @@ class SystemJobAccess(BaseAccess): ''' model = SystemJob -class AdHocCommandAccess(BaseAccess): +class AdHocCommandAccess(BaseAccess): ''' I can only see/run ad hoc commands when: - I am a superuser. @@ -1193,15 +1193,39 @@ class NotificationAccess(BaseAccess): class LabelAccess(BaseAccess): ''' - I can see/use a Label if I have permission to + I can see/use a Label if I have permission to associated organization ''' model = Label def get_queryset(self): - return self.model.objects.distinct().all() + if self.user.is_superuser: + return self.model.objects.all() + return self.model.objects.filter( + organization__in=Organization.accessible_objects(self.user, {'read': True}) + ) + + def can_read(self, obj): + if self.user.is_superuser: + return True + return obj.organization and obj.organization.accessible_by(self.user, {'read': True}) + + def can_add(self, data): + if self.user.is_superuser: + return True + if not data or '_method' in data: # So the browseable API will work? + return True + + org_pk = get_pk_from_dict(data, 'organization') + org = get_object_or_400(Organization, pk=org_pk) + return org.accessible_by(self.user, {'read': True}) + + def can_change(self, obj, data): + if self.user.is_superuser: + return True + return obj.organization and obj.organization.accessible_by(self.user, ALL_PERMISSIONS) def can_delete(self, obj): - return False + return self.can_change(obj, None) class ActivityStreamAccess(BaseAccess): ''' diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py index 405f3fa0e8..11616f2e69 100644 --- a/awx/main/tests/functional/conftest.py +++ b/awx/main/tests/functional/conftest.py @@ -151,6 +151,10 @@ def credential(): def inventory(organization): return organization.inventories.create(name="test-inv") +@pytest.fixture +def label(organization): + return organization.labels.create(name="test-label", description="test-label-desc") + @pytest.fixture def role(): return Role.objects.create(name='role') @@ -219,9 +223,6 @@ def hosts(group): return hosts return rf - - - @pytest.fixture def permissions(): return { diff --git a/awx/main/tests/functional/test_rbac_label.py b/awx/main/tests/functional/test_rbac_label.py new file mode 100644 index 0000000000..62d28dbe84 --- /dev/null +++ b/awx/main/tests/functional/test_rbac_label.py @@ -0,0 +1,50 @@ +import pytest + +from awx.main.access import ( + LabelAccess, +) + +@pytest.mark.django_db +def test_label_get_queryset_user(label, user): + access = LabelAccess(user('user', False)) + label.organization.member_role.members.add(user('user', False)) + assert access.get_queryset().count() == 1 + +@pytest.mark.django_db +def test_label_get_queryset_su(label, user): + access = LabelAccess(user('user', True)) + assert access.get_queryset().count() == 1 + +@pytest.mark.django_db +def test_label_access(label, user): + access = LabelAccess(user('user', False)) + assert not access.can_read(label) + +@pytest.mark.django_db +def test_label_access_superuser(label, user): + access = LabelAccess(user('admin', True)) + + assert access.can_read(label) + assert access.can_change(label, None) + assert access.can_delete(label) + +@pytest.mark.django_db +def test_label_access_admin(label, user): + '''can_change because I am an admin of that org''' + a = user('admin', False) + label.organization.admin_role.members.add(a) + + access = LabelAccess(user('admin', False)) + assert access.can_read(label) + assert access.can_change(label, None) + assert access.can_delete(label) + +@pytest.mark.django_db +def test_label_access_user(label, user): + access = LabelAccess(user('user', False)) + label.organization.member_role.members.add(user('user', False)) + + assert access.can_read(label) + assert not access.can_change(label, None) + assert not access.can_delete(label) +