Merge lp:~jameinel/maas/tags-exposing-nodes into lp:~maas-committers/maas/trunk

Proposed by John A Meinel
Status: Merged
Approved by: Martin Packman
Approved revision: no longer in the source branch.
Merged at revision: 1077
Proposed branch: lp:~jameinel/maas/tags-exposing-nodes
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 137 lines (+77/-4)
4 files modified
src/maasserver/api.py (+1/-4)
src/maasserver/models/tag.py (+18/-0)
src/maasserver/tests/test_api.py (+22/-0)
src/maasserver/tests/test_tag.py (+36/-0)
To merge this branch: bzr merge lp:~jameinel/maas/tags-exposing-nodes
Reviewer Review Type Date Requested Status
Martin Packman (community) Approve
Review via email: mp+126438@code.launchpad.net

Commit message

/tag/<tagname>?op=nodes no longer exposes private nodes.

Respect that if there is a node owner, it doesn't get shown to anyone but superusers and that owner.

Description of the change

See commit message.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Looks good, apart from test_get_nodes_returns_everything_for_superuser not setting `user2.is_superuser = True` which you've now fixed already.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/api.py'
2--- src/maasserver/api.py 2012-09-26 08:54:29 +0000
3+++ src/maasserver/api.py 2012-09-26 12:32:23 +0000
4@@ -1303,10 +1303,7 @@
5 @api_exported('POST')
6 def nodes(self, request, name):
7 """Get the list of nodes that have this tag."""
8- tag = Tag.objects.get_tag_or_404(name=name, user=request.user)
9- # XXX: JAM 2012-09-25 We need to filter the node set returned by the
10- # visibility defined by the user.
11- return tag.node_set.all()
12+ return Tag.objects.get_nodes(name, user=request.user)
13
14 @classmethod
15 def resource_uri(cls, tag=None):
16
17=== modified file 'src/maasserver/models/tag.py'
18--- src/maasserver/models/tag.py 2012-09-23 14:50:34 +0000
19+++ src/maasserver/models/tag.py 2012-09-26 12:32:23 +0000
20@@ -21,6 +21,7 @@
21 CharField,
22 Manager,
23 TextField,
24+ Q,
25 )
26 from django.shortcuts import get_object_or_404
27 from maasserver import DefaultMeta
28@@ -55,6 +56,23 @@
29 tag = get_object_or_404(Tag, name=name)
30 return tag
31
32+ def get_nodes(self, tag_name, user):
33+ """Get the list of nodes that have this tag.
34+
35+ This list is restricted to only nodes that the user has VIEW permission
36+ for.
37+ """
38+ tag = self.get_tag_or_404(name=tag_name, user=user)
39+ # The privacy logic is taken from Node. Note that we could filter in
40+ # python by iterating over all nodes and checking
41+ # user.has_perm(VIEW, node)
42+ # It seems better to do this in the DB, though.
43+ if user.is_superuser:
44+ return tag.node_set.all()
45+ else:
46+ return tag.node_set.filter(
47+ Q(owner__isnull=True) | Q(owner=user))
48+
49
50 class Tag(CleanSave, TimestampedModel):
51 """A `Tag` is a label applied to a `Node`.
52
53=== modified file 'src/maasserver/tests/test_api.py'
54--- src/maasserver/tests/test_api.py 2012-09-26 08:54:29 +0000
55+++ src/maasserver/tests/test_api.py 2012-09-26 12:32:23 +0000
56@@ -2275,6 +2275,28 @@
57 self.assertEqual([node1.system_id],
58 [r['system_id'] for r in parsed_result])
59
60+ def test_POST_nodes_hides_invisible_nodes(self):
61+ user2 = factory.make_user()
62+ node1 = factory.make_node()
63+ node1.set_hardware_details('<node><foo /></node>')
64+ node2 = factory.make_node(status=NODE_STATUS.ALLOCATED, owner=user2)
65+ node2.set_hardware_details('<node><bar /></node>')
66+ tag = factory.make_tag(definition='/node')
67+ tag.populate_nodes()
68+ response = self.client.post(self.get_tag_uri(tag), {'op': 'nodes'})
69+
70+ self.assertEqual(httplib.OK, response.status_code)
71+ parsed_result = json.loads(response.content)
72+ self.assertEqual([node1.system_id],
73+ [r['system_id'] for r in parsed_result])
74+ # However, for the other user, they should see the result
75+ client2 = OAuthAuthenticatedClient(user2)
76+ response = client2.post(self.get_tag_uri(tag), {'op': 'nodes'})
77+ self.assertEqual(httplib.OK, response.status_code)
78+ parsed_result = json.loads(response.content)
79+ self.assertItemsEqual([node1.system_id, node2.system_id],
80+ [r['system_id'] for r in parsed_result])
81+
82 def test_PUT_invalid_definition(self):
83 self.become_admin()
84 node = factory.make_node()
85
86=== modified file 'src/maasserver/tests/test_tag.py'
87--- src/maasserver/tests/test_tag.py 2012-09-26 08:40:33 +0000
88+++ src/maasserver/tests/test_tag.py 2012-09-26 12:32:23 +0000
89@@ -15,6 +15,8 @@
90 from django.db import transaction
91 from django.db.utils import DatabaseError
92 from maastesting.djangotestcase import TransactionTestCase
93+from maasserver.enum import NODE_STATUS
94+from maasserver.models import Tag
95 from maasserver.testing.factory import factory
96 from maasserver.testing.testcase import TestCase
97
98@@ -79,6 +81,40 @@
99 self.assertItemsEqual([tag1.name], node1.tag_names())
100 self.assertItemsEqual([tag2.name], node2.tag_names())
101
102+ def test_get_nodes_returns_unowned_nodes(self):
103+ user1 = factory.make_user()
104+ node1 = factory.make_node()
105+ tag = factory.make_tag()
106+ node1.tags.add(tag)
107+ self.assertItemsEqual([node1], Tag.objects.get_nodes(tag.name, user1))
108+
109+ def test_get_nodes_returns_self_owned_nodes(self):
110+ user1 = factory.make_user()
111+ node1 = factory.make_node(owner=user1)
112+ tag = factory.make_tag()
113+ node1.tags.add(tag)
114+ self.assertItemsEqual([node1], Tag.objects.get_nodes(tag.name, user1))
115+
116+ def test_get_nodes_doesnt_return_other_owned_nodes(self):
117+ user1 = factory.make_user()
118+ user2 = factory.make_user()
119+ node1 = factory.make_node(owner=user1)
120+ tag = factory.make_tag()
121+ node1.tags.add(tag)
122+ self.assertItemsEqual([], Tag.objects.get_nodes(tag.name, user2))
123+
124+ def test_get_nodes_returns_everything_for_superuser(self):
125+ user1 = factory.make_user()
126+ user2 = factory.make_user()
127+ user2.is_superuser = True
128+ node1 = factory.make_node(owner=user1)
129+ node2 = factory.make_node()
130+ tag = factory.make_tag()
131+ node1.tags.add(tag)
132+ node2.tags.add(tag)
133+ self.assertItemsEqual([node1, node2],
134+ Tag.objects.get_nodes(tag.name, user2))
135+
136
137 class TestTagTransactions(TransactionTestCase):
138