Skip to content

Commit b48d3e8

Browse files
authored
fix: form validation on permission protected fields (#479)
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent 547a287 commit b48d3e8

4 files changed

Lines changed: 61 additions & 13 deletions

File tree

component_catalog/tests/test_views.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3566,15 +3566,16 @@ def test_component_catalog_package_form_add(self):
35663566
"usage_policy": policy_approved.pk,
35673567
}
35683568
form = PackageForm(user=self.super_user, data=data)
3569-
self.assertEqual(0, len(form.fields["usage_policy"].queryset))
3569+
self.assertNotIn("usage_policy", form.fields)
35703570
self.assertTrue(form.is_valid())
35713571
package = form.save()
35723572
self.assertIsNone(package.usage_policy)
35733573

35743574
data["filename"] = "with policy"
35753575
self.super_user = add_perm(self.super_user, "change_usage_policy_on_package")
35763576
form = PackageForm(user=self.super_user, data=data)
3577-
self.assertEqual(1, len(form.fields["usage_policy"].queryset))
3577+
self.assertIn("usage_policy", form.fields)
3578+
self.assertQuerySetEqual([policy_approved], form.fields["usage_policy"].queryset)
35783579
self.assertTrue(form.is_valid())
35793580
package = form.save()
35803581
self.assertEqual(policy_approved, package.usage_policy)
@@ -4386,15 +4387,16 @@ def test_component_catalog_component_form_add(self):
43864387
"usage_policy": policy_approved.pk,
43874388
}
43884389
form = ComponentForm(user=self.user, data=data)
4389-
self.assertEqual(0, len(form.fields["usage_policy"].queryset))
4390+
self.assertNotIn("usage_policy", form.fields)
43904391
self.assertTrue(form.is_valid())
43914392
component = form.save()
43924393
self.assertIsNone(component.usage_policy)
43934394

43944395
data["version"] = "with policy"
43954396
self.user = add_perm(self.user, "change_usage_policy_on_component")
43964397
form = ComponentForm(user=self.user, data=data)
4397-
self.assertEqual(1, len(form.fields["usage_policy"].queryset))
4398+
self.assertIn("usage_policy", form.fields)
4399+
self.assertQuerySetEqual([policy_approved], form.fields["usage_policy"].queryset)
43984400
self.assertTrue(form.is_valid())
43994401
component = form.save()
44004402
self.assertEqual(policy_approved, component.usage_policy)
@@ -4413,6 +4415,7 @@ def test_component_catalog_component_form_add(self):
44134415
"homepage_url": "https://nexb.com",
44144416
"configuration_status": status.pk,
44154417
"release_date": "2019-03-01",
4418+
"usage_policy": policy_approved.pk,
44164419
"submit": "Add Component",
44174420
}
44184421
form = ComponentForm(user=self.user, data=data)
@@ -4422,6 +4425,7 @@ def test_component_catalog_component_form_add(self):
44224425
self.assertEqual(status, component.configuration_status)
44234426
self.assertEqual(license1.key, component.license_expression)
44244427
self.assertEqual(["Key1", "Another keyword"], component.keywords)
4428+
self.assertEqual(policy_approved, component.usage_policy)
44254429

44264430
def test_component_catalog_component_form_assigned_packages(self):
44274431
data = {

dje/forms.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,23 @@ class ScopeAndProtectRelationships:
109109
def __init__(self, *args, **kwargs):
110110
super().__init__(*args, **kwargs)
111111

112-
protected_fields = get_protected_fields(self._meta.model, self.user)
113-
self.protected_fields = protected_fields
112+
self.protected_fields = get_protected_fields(self._meta.model, self.user)
113+
114+
# On submit, remove protected fields so they are not validated or saved.
115+
# On display, render them as disabled with an empty queryset.
116+
for name in self.protected_fields:
117+
if self.is_bound:
118+
self.fields.pop(name, None)
119+
elif field := self.fields.get(name):
120+
field.disabled = True
121+
if hasattr(field, "queryset"):
122+
field.queryset = field.queryset.none()
114123

124+
# Scope relational fields to the user's Dataspace
115125
for name, field in self.fields.items():
116126
has_queryset = hasattr(field, "queryset")
117127

118-
if name in protected_fields:
119-
field.disabled = True
120-
if has_queryset:
121-
field.queryset = field.queryset.none()
122-
123-
elif has_queryset and is_dataspace_related(field.queryset.model):
128+
if has_queryset and is_dataspace_related(field.queryset.model):
124129
field.queryset = field.queryset.scope(self.user.dataspace)
125130

126131
related_model = field.queryset.model

product_portfolio/templates/product_portfolio/tables/product_list_table.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
</tr>
7272
{% endfor %}
7373
{% empty %}
74-
<tr><td colspan="8">No results.</td></tr>
74+
<tr><td colspan="9">No results.</td></tr>
7575
{% endfor %}
7676
</tbody>
7777
</table>

product_portfolio/tests/test_views.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2680,6 +2680,45 @@ def test_product_portfolio_product_manage_packages_grid_view_permissions(self):
26802680
response = self.client.get(manage_url)
26812681
self.assertEqual(200, response.status_code)
26822682

2683+
def test_product_portfolio_product_manage_packages_grid_fields_permissions(self):
2684+
add_perms(self.basic_user, ["change_productpackage"])
2685+
assign_perm("view_product", self.basic_user, self.product1)
2686+
assign_perm("change_product", self.basic_user, self.product1)
2687+
self.client.login(username=self.basic_user.username, password="secret")
2688+
2689+
make_product_package(self.product1)
2690+
manage_url = self.product1.get_manage_packages_url()
2691+
response = self.client.get(manage_url)
2692+
2693+
self.assertEqual(200, response.status_code)
2694+
expected = (
2695+
'<select name="form-0-review_status" class="select form-select" disabled'
2696+
' aria-describedby="id_form-0-review_status_helptext" id="id_form-0-review_status">'
2697+
' <option value="" selected>---------</option>'
2698+
"</select>"
2699+
)
2700+
self.assertContains(response, expected, html=True)
2701+
form = response.context["formset"].forms[0]
2702+
self.assertIn("review_status", form.fields)
2703+
self.assertTrue(form.fields["review_status"].disabled)
2704+
2705+
data = {
2706+
"form-TOTAL_FORMS": 1,
2707+
"form-INITIAL_FORMS": 0,
2708+
"form-MIN_NUM_FORMS": 0,
2709+
"form-MAX_NUM_FORMS": 1000,
2710+
"form-0-product": self.product1.pk,
2711+
"form-0-package": self.package1.pk,
2712+
"form-0-object_display": str(self.package1),
2713+
"form-0-review_status": "PROTECTED FIELD",
2714+
"form-0-notes": "Some notes",
2715+
}
2716+
response = self.client.post(manage_url, data, follow=True)
2717+
self.assertContains(response, "Product changes saved.")
2718+
self.assertRedirects(response, manage_url)
2719+
pp2 = ProductPackage.objects.get(product=self.product1, package=self.package1.pk)
2720+
self.assertIsNone(pp2.review_status)
2721+
26832722
def test_product_portfolio_product_manage_packages_grid_view_delete(self):
26842723
self.client.login(username=self.basic_user.username, password="secret")
26852724

0 commit comments

Comments
 (0)