Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not allow changing restricted custom fields #3497

Merged
merged 1 commit into from
Dec 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions src/ralph/lib/custom_fields/forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django import forms
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
Expand All @@ -10,15 +11,38 @@ class CustomFieldValueForm(forms.ModelForm):
class Meta:
fields = ['custom_field', 'value']

def __init__(self, *args, **kwargs):
def __init__(self, request=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
self._replace_value_field()

def _replace_value_field(self):
# replace custom field value field with proper one (ex. select)
if self.instance and self.instance.custom_field_id:
self.fields['value'] = self.instance.custom_field.get_form_field()

def clean(self):
clean_data = super().clean()
custom_field = clean_data.get('custom_field')

if custom_field:
if (
custom_field.managing_group is not None and
custom_field.managing_group not in self.request.user.groups.all() # noqa: E501
):
self.add_error(
'custom_field',
ValidationError(
_(
'Only users from {} group can set '
'this custom field.'
).format(custom_field.managing_group.name),
code='invalid'
)
)

return clean_data


class CustomFieldValueWithClearChildrenForm(CustomFieldValueForm):
clear_children = forms.BooleanField(
Expand All @@ -42,15 +66,16 @@ def save(self, *args, **kwargs):
class CustomFieldValueFormSet(BaseGenericInlineFormSet):

def __init__(self, request=None, queryset=None, *args, **kwargs):
self.request = request
queryset = queryset.filter(
Q(custom_field__managing_group__isnull=True) |
Q(custom_field__managing_group__in=request.user.groups.all())
Q(custom_field__managing_group__in=self.request.user.groups.all())
)

super().__init__(queryset=queryset, *args, **kwargs)

def _construct_form(self, i, **kwargs):
form = super()._construct_form(i, **kwargs)
form = super()._construct_form(i, request=self.request, **kwargs)
# fix for https://code.djangoproject.com/ticket/12028, together with
# model's `_get_unique_checks`, which removes 'content_type' and
# 'object_id' fields from excluded list for unique validation
Expand Down
131 changes: 129 additions & 2 deletions src/ralph/lib/custom_fields/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.test import RequestFactory, TestCase
from rest_framework.status import HTTP_200_OK

from ralph.accounts.tests.factories import GroupFactory

from ..models import CustomField, CustomFieldTypes, CustomFieldValue
from .models import ModelA, ModelB, SomeModel

Expand Down Expand Up @@ -258,7 +260,7 @@ def test_custom_field_not_in_form_for_nonmatching_managing_group(self):

filled_in_custom_field_forms = [
form
for form in response.context_data['inline_admin_formsets'][0].formset.forms
for form in response.context_data['inline_admin_formsets'][0].formset.forms # noqa: E501
if form.fields['id'].initial is not None
]

Expand All @@ -284,7 +286,7 @@ def test_custom_field_in_form_for_matching_managing_group(self):

filled_in_custom_field_forms = [
form
for form in response.context_data['inline_admin_formsets'][0].formset.forms
for form in response.context_data['inline_admin_formsets'][0].formset.forms # noqa: E501
if form.fields['id'].initial is not None
]

Expand All @@ -295,3 +297,128 @@ def test_custom_field_in_form_for_matching_managing_group(self):
self.cfv1.id,
form.fields['id'].initial
)

def test_add_new_custom_field_value_succeeds_with_matching_managing_group(self): # noqa: E501
group = GroupFactory()

self.user.groups.add(group)
self.custom_field_choices.managing_group = group

self.user.save()
self.custom_field_choices.save()

data = {
'id': self.sm1.id,
'name': self.sm1.name,
}
data_custom_fields = {
'TOTAL_FORMS': 3,
'INITIAL_FORMS': 1,
'0-id': self.cfv1.id,
'0-custom_field': self.custom_field_str.id,
'0-value': 'sample_value',
'1-id': '',
'1-custom_field': self.custom_field_choices.id,
'1-value': 'qwerty',
}
data.update(self._prepare_inline_data(data_custom_fields))

response = self.client.post(
self.sm1.get_absolute_url(),
data,
follow=True
)
self.assertEqual(response.status_code, HTTP_200_OK)

custom_field_qs = CustomFieldValue.objects.filter(
object_id=self.sm1.id,
content_type=ContentType.objects.get_for_model(SomeModel),
custom_field=self.custom_field_choices,
value='qwerty'
)
self.assertTrue(custom_field_qs.exists())

def test_add_new_custom_field_value_fails_with_nonmatching_managing_group(self): # noqa: E501
group = GroupFactory()

self.custom_field_choices.managing_group = group
self.custom_field_choices.save()

data = {
'id': self.sm1.id,
'name': self.sm1.name,
}
data_custom_fields = {
'TOTAL_FORMS': 3,
'INITIAL_FORMS': 1,
'0-id': self.cfv1.id,
'0-custom_field': self.custom_field_str.id,
'0-value': 'sample_value',
'1-id': '',
'1-custom_field': self.custom_field_choices.id,
'1-value': 'qwerty',
}
data.update(self._prepare_inline_data(data_custom_fields))

response = self.client.post(
self.sm1.get_absolute_url(),
data,
follow=True
)
self.assertEqual(response.status_code, HTTP_200_OK)

expected_error_message = (
'<ul class="errorlist"><li>'
'Only users from {} group can set this custom field.</li></ul>'
).format(group.name)
self.assertIn('errors', response.context_data)
self.assertIn(expected_error_message, response.context_data['errors'])

custom_field_qs = CustomFieldValue.objects.filter(
object_id=self.sm1.id,
content_type=ContentType.objects.get_for_model(SomeModel),
custom_field=self.custom_field_choices,
value='qwerty'
)
self.assertFalse(custom_field_qs.exists())

def test_edit_custom_field_value_fails_with_nonmatching_managing_group(self): # noqa: E501
group = GroupFactory()

self.custom_field_str.managing_group = group
self.custom_field_str.save()

data = {
'id': self.sm1.id,
'name': self.sm1.name,
}
data_custom_fields = {
'TOTAL_FORMS': 2,
'INITIAL_FORMS': 1,
'0-id': self.cfv1.id,
'0-custom_field': self.custom_field_str.id,
'0-value': 'NEW-VALUE',
}
data.update(self._prepare_inline_data(data_custom_fields))

response = self.client.post(
self.sm1.get_absolute_url(),
data,
follow=True
)
self.assertEqual(response.status_code, HTTP_200_OK)

expected_error_message = (
'<ul class="errorlist"><li>'
'Only users from {} group can set this custom field.</li></ul>'
).format(group.name)
self.assertIn('errors', response.context_data)
self.assertIn(expected_error_message, response.context_data['errors'])

custom_field_qs = CustomFieldValue.objects.filter(
object_id=self.sm1.id,
content_type=ContentType.objects.get_for_model(SomeModel),
custom_field=self.custom_field_str,
value='sample_value'
)
self.assertTrue(custom_field_qs.exists())