summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Makogon <lildee1991@gmail.com>2016-12-09 22:51:12 +0200
committerGitHub <noreply@github.com>2016-12-09 22:51:12 +0200
commit6f6a5e6f467ac17f651e7a64c6d20084df07febe (patch)
tree270c0993430cf78d6e0843525731f11e722ef8e8
parentb9e265533626501b2dac99419c8d8534bf24e2da (diff)
parent262903983f61ad61ab4a067ee983db3522d8c60b (diff)
Merge pull request #23 from denismakogon/issue/22
Issue #22: Make public route execution validation more strict
-rw-r--r--picasso/api/controllers/runnable.py39
-rw-r--r--picasso/tests/common/routes.py32
-rw-r--r--picasso/tests/functional/base.py5
-rw-r--r--picasso/tests/functional/test_routes.py7
-rw-r--r--picasso/tests/integration/test_routes.py4
-rw-r--r--tox.ini4
6 files changed, 82 insertions, 9 deletions
diff --git a/picasso/api/controllers/runnable.py b/picasso/api/controllers/runnable.py
index a183a7d..338ac21 100644
--- a/picasso/api/controllers/runnable.py
+++ b/picasso/api/controllers/runnable.py
@@ -18,6 +18,7 @@ from aioservice.http import controller
18from aioservice.http import requests 18from aioservice.http import requests
19 19
20from ...common import config 20from ...common import config
21from ...models import app as app_model
21 22
22 23
23class RunnableMixin(object): 24class RunnableMixin(object):
@@ -88,9 +89,29 @@ class PublicRunnableV1Controller(controller.ServiceController,
88 description: successful operation. Return "runnable" JSON 89 description: successful operation. Return "runnable" JSON
89 "404": 90 "404":
90 description: App does not exist 91 description: App does not exist
91 "404": 92 "403":
92 description: App route does not exist 93 description: Unable to execute private route
93 """ 94 """
95 app = request.match_info.get('app')
96 path = request.match_info.get('route')
97 routes = await app_model.Routes.find_by(app_name=app, path=path)
98
99 if not routes:
100 return web.json_response(data={
101 "error": {
102 "message": "Route {0} not found".format(app),
103 }
104 }, status=404)
105 route = routes.pop()
106
107 if not route.public:
108 return web.json_response(data={
109 "error": {
110 "message": "Unable to execute private "
111 "route {0}".format(path)
112 }
113 }, status=403)
114
94 return await super(PublicRunnableV1Controller, 115 return await super(PublicRunnableV1Controller,
95 self).run(request, **kwargs) 116 self).run(request, **kwargs)
96 117
@@ -118,9 +139,19 @@ class RunnableV1Controller(controller.ServiceController,
118 "200": 139 "200":
119 description: successful operation. Return "runnable" JSON 140 description: successful operation. Return "runnable" JSON
120 "404": 141 "404":
121 description: App does not exist 142 description: App not found
122 "404": 143 "404":
123 description: App route does not exist 144 description: App route not found
124 """ 145 """
146 app = request.match_info.get('app')
147 project_id = request.match_info.get('project_id')
148
149 if not (await app_model.Apps.exists(app, project_id)):
150 return web.json_response(data={
151 "error": {
152 "message": "App {0} not found".format(app),
153 }
154 }, status=404)
155
125 return await super(RunnableV1Controller, 156 return await super(RunnableV1Controller,
126 self).run(request, **kwargs) 157 self).run(request, **kwargs)
diff --git a/picasso/tests/common/routes.py b/picasso/tests/common/routes.py
index 89ead7f..ab76c59 100644
--- a/picasso/tests/common/routes.py
+++ b/picasso/tests/common/routes.py
@@ -17,14 +17,19 @@ import json as jsonlib
17 17
18 18
19@contextlib.contextmanager 19@contextlib.contextmanager
20def setup_execute(self, app_name): 20def setup_execute(self, app_name, create_public_route=False):
21 app, _ = self.testloop.run_until_complete( 21 app, _ = self.testloop.run_until_complete(
22 self.test_client.apps.create(app_name) 22 self.test_client.apps.create(app_name)
23 ) 23 )
24 new_app_name = app["app"]["name"] 24 new_app_name = app["app"]["name"]
25 route_data = self.route_data
26
27 if create_public_route:
28 route_data.update(is_public="true")
29
25 route, _ = self.testloop.run_until_complete( 30 route, _ = self.testloop.run_until_complete(
26 self.test_client.routes.create( 31 self.test_client.routes.create(
27 new_app_name, **self.route_data) 32 new_app_name, **route_data)
28 ) 33 )
29 self.testloop.run_until_complete( 34 self.testloop.run_until_complete(
30 self.test_client.routes.update( 35 self.test_client.routes.update(
@@ -199,7 +204,8 @@ class AppRoutesTestSuite(object):
199 self.assertEqual(200, status) 204 self.assertEqual(200, status)
200 205
201 def execute_public(self): 206 def execute_public(self):
202 with setup_execute(self, "execute_public") as app_name: 207 with setup_execute(self, "execute_public",
208 create_public_route=True) as app_name:
203 result, status = self.testloop.run_until_complete( 209 result, status = self.testloop.run_until_complete(
204 self.test_client.routes.execute_public( 210 self.test_client.routes.execute_public(
205 app_name, self.route_data["path"] 211 app_name, self.route_data["path"]
@@ -207,3 +213,23 @@ class AppRoutesTestSuite(object):
207 ) 213 )
208 self.assertIsNotNone(result) 214 self.assertIsNotNone(result)
209 self.assertEqual(200, status) 215 self.assertEqual(200, status)
216
217 def fail_to_execute_private_as_public(self):
218 with setup_execute(self, "fail_to_execute_"
219 "private_as_public") as app_name:
220 _, status = self.testloop.run_until_complete(
221 self.test_client.routes.execute_public(
222 app_name, self.route_data["path"]
223 )
224 )
225 self.assertEqual(403, status)
226
227 def fail_to_run_app_from_other_project(self):
228 with setup_execute(self, "fail_to_run_app_"
229 "from_other_project") as app_name:
230 _, status = self.testloop.run_until_complete(
231 self.other_test_client.routes.execute_public(
232 app_name, self.route_data["path"]
233 )
234 )
235 self.assertEqual(404, status)
diff --git a/picasso/tests/functional/base.py b/picasso/tests/functional/base.py
index cf88952..4e3db03 100644
--- a/picasso/tests/functional/base.py
+++ b/picasso/tests/functional/base.py
@@ -74,12 +74,17 @@ class FunctionalTestsBase(base.PicassoTestsBase, testtools.TestCase):
74 ) 74 )
75 75
76 self.project_id = str(uuid.uuid4()).replace("-", "") 76 self.project_id = str(uuid.uuid4()).replace("-", "")
77 self.other_project_id = str(uuid.uuid4()).replace("-", "")
78
77 self.test_client = client.ProjectBoundTestClient( 79 self.test_client = client.ProjectBoundTestClient(
78 self.testapp, self.project_id) 80 self.testapp, self.project_id)
81 self.other_test_client = client.ProjectBoundTestClient(
82 self.testapp, self.other_project_id)
79 83
80 self.testloop.run_until_complete(self.test_client.start_server()) 84 self.testloop.run_until_complete(self.test_client.start_server())
81 super(FunctionalTestsBase, self).setUp() 85 super(FunctionalTestsBase, self).setUp()
82 86
83 def tearDown(self): 87 def tearDown(self):
84 self.testloop.run_until_complete(self.test_client.close()) 88 self.testloop.run_until_complete(self.test_client.close())
89 self.testloop.run_until_complete(self.other_test_client.close())
85 super(FunctionalTestsBase, self).tearDown() 90 super(FunctionalTestsBase, self).tearDown()
diff --git a/picasso/tests/functional/test_routes.py b/picasso/tests/functional/test_routes.py
index 62f9954..ee48539 100644
--- a/picasso/tests/functional/test_routes.py
+++ b/picasso/tests/functional/test_routes.py
@@ -49,3 +49,10 @@ class TestAppRoutes(base.FunctionalTestsBase,
49 49
50 def test_public_execution(self): 50 def test_public_execution(self):
51 super(TestAppRoutes, self).execute_private() 51 super(TestAppRoutes, self).execute_private()
52
53 def test_fail_to_execute_private_route(self):
54 super(TestAppRoutes, self).fail_to_execute_private_as_public()
55
56 def test_fail_to_run_app_from_other_project(self):
57 super(TestAppRoutes,
58 self).fail_to_run_app_from_other_project()
diff --git a/picasso/tests/integration/test_routes.py b/picasso/tests/integration/test_routes.py
index 52a9fd2..7c76c3c 100644
--- a/picasso/tests/integration/test_routes.py
+++ b/picasso/tests/integration/test_routes.py
@@ -55,3 +55,7 @@ class TestIntegrationAppRoutes(base.FunctionalTestsBase,
55 55
56 def test_public_execution(self): 56 def test_public_execution(self):
57 super(TestIntegrationAppRoutes, self).execute_private() 57 super(TestIntegrationAppRoutes, self).execute_private()
58
59 def test_fail_to_execute_private_route(self):
60 super(TestIntegrationAppRoutes,
61 self).fail_to_execute_private_as_public()
diff --git a/tox.ini b/tox.ini
index c46647b..79e189b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -32,10 +32,10 @@ commands = flake8
32commands = {posargs} 32commands = {posargs}
33 33
34[testenv:py35-integration] 34[testenv:py35-integration]
35commands = pytest --tb=long --capture=sys --cov=picasso --capture=fd {toxinidir}/picasso/tests/integration 35commands = pytest -v --tb=long --capture=sys --cov=picasso --capture=fd {toxinidir}/picasso/tests/integration
36 36
37[testenv:py35-functional] 37[testenv:py35-functional]
38commands = pytest --tb=long --capture=sys --cov=picasso --capture=fd {toxinidir}/picasso/tests/functional 38commands = pytest -v --tb=long --capture=sys --cov=picasso --capture=fd {toxinidir}/picasso/tests/functional
39 39
40[testenv:py35-functional-regression] 40[testenv:py35-functional-regression]
41commands = {toxinidir}/scripts/test_regression.sh functional {posargs} 41commands = {toxinidir}/scripts/test_regression.sh functional {posargs}