← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/maas/maas-remove-enlist-autoaccept2 into lp:maas

 

Raphaël Badin has proposed merging lp:~rvb/maas/maas-remove-enlist-autoaccept2 into lp:maas with lp:~rvb/maas/maas-remove-enlist-autoaccept as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~rvb/maas/maas-remove-enlist-autoaccept2/+merge/100627

This branch change the API call 'accept'.  It now requires to have NODE_PERMISSION.ADMIN on the accepted nodes.  That is now in sync with the permission checked performed in the UI.
-- 
https://code.launchpad.net/~rvb/maas/maas-remove-enlist-autoaccept2/+merge/100627
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/maas-remove-enlist-autoaccept2 into lp:maas.
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py	2012-04-03 15:14:19 +0000
+++ src/maasserver/api.py	2012-04-03 15:14:19 +0000
@@ -432,9 +432,9 @@
         """Create a new Node.
 
         Adding a server to a MAAS puts it on a path that will wipe its disks
-        and re-install its operating system.  In anonymous enlistment,
-        therefore, the node is held in the "Declared" state for approval by a
-        MAAS user.
+        and re-install its operating system.  In anonymous enlistment and when
+        the enlistment is done by a non-admin, the node is held in the
+        "Declared" state for approval by a MAAS admin.
         """
         return create_node(request)
 
@@ -473,21 +473,22 @@
     def new(self, request):
         """Create a new Node.
 
-        When a node has been added to MAAS by a logged-in MAAS user, it is
+        When a node has been added to MAAS by an admin MAAS user, it is
         ready for allocation to services running on the MAAS.
         """
         node = create_node(request)
-        node.accept_enlistment()
+        if request.user.is_superuser:
+            node.accept_enlistment()
         return node
 
     @api_exported('accept', 'POST')
     def accept(self, request):
         """Accept declared nodes into the MAAS.
 
-        Nodes can be enlisted in the MAAS anonymously, as opposed to by a
-        logged-in user, at the nodes' own request.  These nodes are held in
-        the Declared state; a MAAS user must first verify the authenticity of
-        these enlistments, and accept them.
+        Nodes can be enlisted in the MAAS anonymously or by non-admin users,
+        as opposed to by an admin.  These nodes are held in the Declared
+        state; a MAAS admin must first verify the authenticity of these
+        enlistments, and accept them.
 
         Enlistments can be accepted en masse, by passing multiple nodes to
         this call.  Accepting an already accepted node is not an error, but
@@ -500,11 +501,21 @@
             excluded from the result.
         """
         system_ids = set(request.POST.getlist('nodes'))
-        nodes = Node.objects.filter(system_id__in=system_ids)
-        found_ids = set(node.system_id for node in nodes)
+        # Check the existence of these nodes first.
+        existing_ids = set(Node.objects.filter().values_list(
+            'system_id', flat=True))
+        if len(existing_ids) < len(system_ids):
+            raise MAASAPIBadRequest(
+                "Unknown node(s): %s." % ', '.join(system_ids - existing_ids))
+        # Make sure that the user has the required permission.
+        nodes = Node.objects.get_nodes(
+            request.user, perm=NODE_PERMISSION.ADMIN, ids=system_ids)
+        ids = set(node.system_id for node in nodes)
         if len(nodes) < len(system_ids):
-            raise MAASAPIBadRequest(
-                "Unknown node(s): %s" % ', '.join(system_ids - found_ids))
+            raise PermissionDenied(
+                "You don't have the required permission to accept the "
+                "following node(s): %s." % (
+                    ', '.join(system_ids - ids)))
         return filter(None, [node.accept_enlistment() for node in nodes])
 
     @api_exported('list', 'GET')

=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py	2012-04-03 10:19:47 +0000
+++ src/maasserver/tests/test_api.py	2012-04-03 15:14:19 +0000
@@ -690,7 +690,7 @@
             self.get_uri('nodes/'),
             {
                 'op': 'new',
-                'hostname': 'diane',
+                'hostname': factory.getRandomString(),
                 'architecture': architecture,
                 'after_commissioning_action': '2',
                 'mac_addresses': ['aa:bb:cc:dd:ee:ff', '22:bb:cc:dd:ee:ff'],
@@ -698,10 +698,28 @@
 
         self.assertEqual(httplib.OK, response.status_code)
 
-    def test_POST_new_when_logged_in_creates_node_in_ready_state(self):
-        # When a logged-in user enlists a node, it goes into the Ready
-        # state.
-        # This will change once we start doing proper commissioning.
+    def test_POST_new_when_logged_in_creates_node_in_declared_state(self):
+        # When a user enlists a node, it goes into the Declared state.
+        # This will change once we start doing proper commissioning.
+        response = self.client.post(
+            self.get_uri('nodes/'),
+            {
+                'op': 'new',
+                'hostname': factory.getRandomString(),
+                'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES),
+                'after_commissioning_action': '2',
+                'mac_addresses': ['aa:bb:cc:dd:ee:ff'],
+            })
+        self.assertEqual(httplib.OK, response.status_code)
+        system_id = json.loads(response.content)['system_id']
+        self.assertEqual(
+            NODE_STATUS.DECLARED,
+            Node.objects.get(system_id=system_id).status)
+
+    def test_POST_new_when_admin_creates_node_in_ready_state(self):
+        # When an admin user enlists a node, it goes into the Ready state.
+        # This will change once we start doing proper commissioning.
+        self.become_admin()
         response = self.client.post(
             self.get_uri('nodes/'),
             {
@@ -965,6 +983,7 @@
     def test_POST_accept_gets_node_out_of_declared_state(self):
         # This will change when we add provisioning.  Until then,
         # acceptance gets a node straight to Ready state.
+        self.become_admin()
         target_state = NODE_STATUS.READY
 
         node = factory.make_node(status=NODE_STATUS.DECLARED)
@@ -985,6 +1004,7 @@
             (httplib.OK, "[]"), (response.status_code, response.content))
 
     def test_POST_accept_rejects_impossible_state_changes(self):
+        self.become_admin()
         acceptable_states = set([
             NODE_STATUS.DECLARED,
             NODE_STATUS.COMMISSIONING,
@@ -1019,16 +1039,29 @@
             self.assertIn(NODE_STATUS_CHOICES_DICT[status], response.content)
 
     def test_POST_accept_fails_if_node_does_not_exist(self):
+        self.become_admin()
         node_id = factory.getRandomString()
         response = self.client.post(
             self.get_uri('nodes/'), {'op': 'accept', 'nodes': [node_id]})
         self.assertEqual(
-            (httplib.BAD_REQUEST, "Unknown node(s): %s" % node_id),
+            (httplib.BAD_REQUEST, "Unknown node(s): %s." % node_id),
+            (response.status_code, response.content))
+
+    def test_POST_accept_fails_if_not_admin(self):
+        node = factory.make_node(status=NODE_STATUS.DECLARED)
+        response = self.client.post(
+            self.get_uri('nodes/'),
+            {'op': 'accept', 'nodes': [node.system_id]})
+        self.assertEqual(
+            (httplib.FORBIDDEN,
+                "You don't have the required permission to accept the "
+                "following node(s): %s." % node.system_id),
             (response.status_code, response.content))
 
     def test_POST_accept_accepts_multiple_nodes(self):
         # This will change when we add provisioning.  Until then,
         # acceptance gets a node straight to Ready state.
+        self.become_admin()
         target_state = NODE_STATUS.READY
 
         nodes = [
@@ -1045,6 +1078,7 @@
             [reload_object(node).status for node in nodes])
 
     def test_POST_accept_returns_actually_accepted_nodes(self):
+        self.become_admin()
         acceptable_nodes = [
             factory.make_node(status=NODE_STATUS.DECLARED)
             for counter in range(2)