Merge lp:~allenap/maas/version-without-auth--bug-1583715--1.9 into lp:maas/1.9

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 4582
Proposed branch: lp:~allenap/maas/version-without-auth--bug-1583715--1.9
Merge into: lp:maas/1.9
Diff against target: 206 lines (+110/-5)
4 files modified
src/maasserver/api/support.py (+26/-0)
src/maasserver/api/tests/test_support.py (+67/-1)
src/maasserver/api/tests/test_version.py (+15/-3)
src/maasserver/urls_api.py (+2/-1)
To merge this branch: bzr merge lp:~allenap/maas/version-without-auth--bug-1583715--1.9
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+295837@code.launchpad.net

Commit message

Backport r5039 from lp:maas. Ensure that restricted resources also perform meaningful authentication of clients.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Backport; self-reviewing.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/support.py'
--- src/maasserver/api/support.py 2016-04-13 16:34:23 +0000
+++ src/maasserver/api/support.py 2016-05-26 14:12:20 +0000
@@ -25,6 +25,7 @@
25from django.http import Http40425from django.http import Http404
26from maasserver.api.doc import get_api_description_hash26from maasserver.api.doc import get_api_description_hash
27from maasserver.exceptions import MAASAPIBadRequest27from maasserver.exceptions import MAASAPIBadRequest
28from piston.authentication import NoAuthentication
28from piston.emitters import Emitter29from piston.emitters import Emitter
29from piston.handler import (30from piston.handler import (
30 AnonymousBaseHandler,31 AnonymousBaseHandler,
@@ -66,10 +67,35 @@
66 else:67 else:
67 raise68 raise
6869
70 @property
71 def is_authentication_attempted(self):
72 """Will use of this resource attempt to authenticate the client?
73
74 For example, `None`, ``[]``, and :class:`NoAuthentication` are all
75 examples of authentication handlers that do *not* count.
76 """
77 return len(self.authentication) != 0 and not any(
78 isinstance(auth, NoAuthentication) for auth in self.authentication)
79
6980
70class RestrictedResource(OperationsResource):81class RestrictedResource(OperationsResource):
71 """A resource that's restricted to active users."""82 """A resource that's restricted to active users."""
7283
84 def __init__(self, handler, authentication):
85 """A value for `authentication` MUST be provided AND be meaningful.
86
87 This prevents the situation where none of the following are restricted
88 at all::
89
90 handler = RestrictedResource(HandlerClass)
91 handler = RestrictedResource(HandlerClass, authentication=None)
92 handler = RestrictedResource(HandlerClass, authentication=[])
93
94 """
95 super(RestrictedResource, self).__init__(handler, authentication)
96 if not self.is_authentication_attempted:
97 raise AssertionError("Authentication must be attempted.")
98
73 def authenticate(self, request, rm):99 def authenticate(self, request, rm):
74 actor, anonymous = super(100 actor, anonymous = super(
75 RestrictedResource, self).authenticate(request, rm)101 RestrictedResource, self).authenticate(request, rm)
76102
=== modified file 'src/maasserver/api/tests/test_support.py'
--- src/maasserver/api/tests/test_support.py 2015-04-21 22:51:42 +0000
+++ src/maasserver/api/tests/test_support.py 2016-05-26 14:12:20 +0000
@@ -23,7 +23,10 @@
23from maasserver.api.doc import get_api_description_hash23from maasserver.api.doc import get_api_description_hash
24from maasserver.api.support import (24from maasserver.api.support import (
25 admin_method,25 admin_method,
26 AdminRestrictedResource,
26 OperationsHandlerMixin,27 OperationsHandlerMixin,
28 OperationsResource,
29 RestrictedResource,
27)30)
28from maasserver.models.config import (31from maasserver.models.config import (
29 Config,32 Config,
@@ -37,7 +40,17 @@
37 Mock,40 Mock,
38 sentinel,41 sentinel,
39)42)
40from testtools.matchers import Equals43from piston.authentication import NoAuthentication
44from testtools.matchers import (
45 Equals,
46 Is,
47)
48
49
50class StubHandler:
51 """A stub handler class that breaks when called."""
52 def __call__(self, request):
53 raise AssertionError("Do not call the stub handler.")
4154
4255
43class TestOperationsResource(APITestCase):56class TestOperationsResource(APITestCase):
@@ -76,6 +89,59 @@
76 response["X-MAAS-API-Hash"],89 response["X-MAAS-API-Hash"],
77 Equals(get_api_description_hash()))90 Equals(get_api_description_hash()))
7891
92 def test_authenticated_is_False_when_no_authentication_provided(self):
93 resource = OperationsResource(StubHandler)
94 self.assertThat(resource.is_authentication_attempted, Is(False))
95
96 def test_authenticated_is_False_when_authentication_is_empty(self):
97 resource = OperationsResource(StubHandler, authentication=[])
98 self.assertThat(resource.is_authentication_attempted, Is(False))
99
100 def test_authenticated_is_False_when_authentication_is_NoAuthn(self):
101 resource = OperationsResource(
102 StubHandler, authentication=NoAuthentication())
103 self.assertThat(resource.is_authentication_attempted, Is(False))
104
105 def test_authenticated_is_True_when_authentication_is_provided(self):
106 resource = OperationsResource(
107 StubHandler, authentication=sentinel.authentication)
108 self.assertThat(resource.is_authentication_attempted, Is(True))
109
110
111class TestRestrictedResources(APITestCase):
112 """Tests for `RestrictedResource` and `AdminRestrictedResource`."""
113
114 scenarios = (
115 ("user", dict(resource_type=RestrictedResource)),
116 ("admin", dict(resource_type=AdminRestrictedResource)),
117 )
118
119 def test_authentication_must_not_be_None(self):
120 error = self.assertRaises(
121 AssertionError, self.resource_type, StubHandler,
122 authentication=None)
123 self.assertThat(unicode(error), Equals(
124 "Authentication must be attempted."))
125
126 def test_authentication_must_be_non_empty(self):
127 error = self.assertRaises(
128 AssertionError, self.resource_type, StubHandler,
129 authentication=[])
130 self.assertThat(unicode(error), Equals(
131 "Authentication must be attempted."))
132
133 def test_authentication_must_be_meaningful(self):
134 error = self.assertRaises(
135 AssertionError, self.resource_type, StubHandler,
136 authentication=NoAuthentication())
137 self.assertThat(unicode(error), Equals(
138 "Authentication must be attempted."))
139
140 def test_authentication_is_okay(self):
141 resource = self.resource_type(
142 StubHandler, authentication=sentinel.authentication)
143 self.assertThat(resource.is_authentication_attempted, Is(True))
144
79145
80class TestAdminMethodDecorator(APITestCase):146class TestAdminMethodDecorator(APITestCase):
81147
82148
=== modified file 'src/maasserver/api/tests/test_version.py'
--- src/maasserver/api/tests/test_version.py 2015-06-24 14:20:57 +0000
+++ src/maasserver/api/tests/test_version.py 2016-05-26 14:12:20 +0000
@@ -1,4 +1,4 @@
1# Copyright 2014-2015 Canonical Ltd. This software is licensed under the1# Copyright 2014-2016 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test maasserver API version."""4"""Test maasserver API version."""
@@ -18,19 +18,31 @@
18import httplib18import httplib
19import json19import json
2020
21from django.contrib.auth.models import AnonymousUser
21from django.core.urlresolvers import reverse22from django.core.urlresolvers import reverse
22from maasserver.api.version import API_CAPABILITIES_LIST23from maasserver.api.version import API_CAPABILITIES_LIST
24from maasserver.testing.api import MultipleUsersScenarios
25from maasserver.testing.factory import factory
23from maasserver.testing.testcase import MAASServerTestCase26from maasserver.testing.testcase import MAASServerTestCase
24from maasserver.utils import version as version_module27from maasserver.utils import version as version_module
2528
2629
27class TestFindingResources(MAASServerTestCase):30class TestVersionAPIBasics(MAASServerTestCase):
28 """Tests for /version/ API."""31 """Basic tests for /version/ API."""
2932
30 def test_handler_path(self):33 def test_handler_path(self):
31 self.assertEqual(34 self.assertEqual(
32 '/api/1.0/version/', reverse('version_handler'))35 '/api/1.0/version/', reverse('version_handler'))
3336
37
38class TestVersionAPI(MultipleUsersScenarios, MAASServerTestCase):
39 """Tests for /version/ API."""
40
41 scenarios = [
42 ('anon', dict(userfactory=AnonymousUser)),
43 ('user', dict(userfactory=factory.make_User)),
44 ]
45
34 def test_GET_returns_details(self):46 def test_GET_returns_details(self):
35 mock_apt = self.patch(version_module, "get_version_from_apt")47 mock_apt = self.patch(version_module, "get_version_from_apt")
36 mock_apt.return_value = "1.8.0~alpha4+bzr356-0ubuntu1"48 mock_apt.return_value = "1.8.0~alpha4+bzr356-0ubuntu1"
3749
=== modified file 'src/maasserver/urls_api.py'
--- src/maasserver/urls_api.py 2015-12-07 14:25:30 +0000
+++ src/maasserver/urls_api.py 2016-05-26 14:12:20 +0000
@@ -132,6 +132,7 @@
132)132)
133from maasserver.api.support import (133from maasserver.api.support import (
134 AdminRestrictedResource,134 AdminRestrictedResource,
135 OperationsResource,
135 RestrictedResource,136 RestrictedResource,
136)137)
137from maasserver.api.tags import (138from maasserver.api.tags import (
@@ -219,7 +220,7 @@
219 BootImagesHandler, authentication=api_auth)220 BootImagesHandler, authentication=api_auth)
220tag_handler = RestrictedResource(TagHandler, authentication=api_auth)221tag_handler = RestrictedResource(TagHandler, authentication=api_auth)
221tags_handler = RestrictedResource(TagsHandler, authentication=api_auth)222tags_handler = RestrictedResource(TagsHandler, authentication=api_auth)
222version_handler = RestrictedResource(VersionHandler)223version_handler = OperationsResource(VersionHandler)
223node_results_handler = RestrictedResource(224node_results_handler = RestrictedResource(
224 NodeResultsHandler, authentication=api_auth)225 NodeResultsHandler, authentication=api_auth)
225sshkey_handler = RestrictedResource(SSHKeyHandler, authentication=api_auth)226sshkey_handler = RestrictedResource(SSHKeyHandler, authentication=api_auth)

Subscribers

People subscribed via source and target branches