Browse Source

Merge pull request #23 from denismakogon/issue/22

Issue #22: Make public route execution validation more strict
changes/20/418020/1
Denis Makogon 2 years ago
parent
commit
6f6a5e6f46

+ 35
- 4
picasso/api/controllers/runnable.py View File

@@ -18,6 +18,7 @@ from aioservice.http import controller
18 18
 from aioservice.http import requests
19 19
 
20 20
 from ...common import config
21
+from ...models import app as app_model
21 22
 
22 23
 
23 24
 class RunnableMixin(object):
@@ -88,9 +89,29 @@ class PublicRunnableV1Controller(controller.ServiceController,
88 89
                 description: successful operation. Return "runnable" JSON
89 90
             "404":
90 91
                 description: App does not exist
91
-            "404":
92
-                description: App route does not exist
92
+            "403":
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 115
         return await super(PublicRunnableV1Controller,
95 116
                            self).run(request, **kwargs)
96 117
 
@@ -118,9 +139,19 @@ class RunnableV1Controller(controller.ServiceController,
118 139
             "200":
119 140
                 description: successful operation. Return "runnable" JSON
120 141
             "404":
121
-                description: App does not exist
142
+                description: App not found
122 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 156
         return await super(RunnableV1Controller,
126 157
                            self).run(request, **kwargs)

+ 29
- 3
picasso/tests/common/routes.py View File

@@ -17,14 +17,19 @@ import json as jsonlib
17 17
 
18 18
 
19 19
 @contextlib.contextmanager
20
-def setup_execute(self, app_name):
20
+def setup_execute(self, app_name, create_public_route=False):
21 21
     app, _ = self.testloop.run_until_complete(
22 22
         self.test_client.apps.create(app_name)
23 23
     )
24 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 30
     route, _ = self.testloop.run_until_complete(
26 31
         self.test_client.routes.create(
27
-            new_app_name, **self.route_data)
32
+            new_app_name, **route_data)
28 33
     )
29 34
     self.testloop.run_until_complete(
30 35
         self.test_client.routes.update(
@@ -199,7 +204,8 @@ class AppRoutesTestSuite(object):
199 204
             self.assertEqual(200, status)
200 205
 
201 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 209
             result, status = self.testloop.run_until_complete(
204 210
                 self.test_client.routes.execute_public(
205 211
                     app_name, self.route_data["path"]
@@ -207,3 +213,23 @@ class AppRoutesTestSuite(object):
207 213
             )
208 214
             self.assertIsNotNone(result)
209 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)

+ 5
- 0
picasso/tests/functional/base.py View File

@@ -74,12 +74,17 @@ class FunctionalTestsBase(base.PicassoTestsBase, testtools.TestCase):
74 74
         )
75 75
 
76 76
         self.project_id = str(uuid.uuid4()).replace("-", "")
77
+        self.other_project_id = str(uuid.uuid4()).replace("-", "")
78
+
77 79
         self.test_client = client.ProjectBoundTestClient(
78 80
             self.testapp, self.project_id)
81
+        self.other_test_client = client.ProjectBoundTestClient(
82
+            self.testapp, self.other_project_id)
79 83
 
80 84
         self.testloop.run_until_complete(self.test_client.start_server())
81 85
         super(FunctionalTestsBase, self).setUp()
82 86
 
83 87
     def tearDown(self):
84 88
         self.testloop.run_until_complete(self.test_client.close())
89
+        self.testloop.run_until_complete(self.other_test_client.close())
85 90
         super(FunctionalTestsBase, self).tearDown()

+ 7
- 0
picasso/tests/functional/test_routes.py View File

@@ -49,3 +49,10 @@ class TestAppRoutes(base.FunctionalTestsBase,
49 49
 
50 50
     def test_public_execution(self):
51 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()

+ 4
- 0
picasso/tests/integration/test_routes.py View File

@@ -55,3 +55,7 @@ class TestIntegrationAppRoutes(base.FunctionalTestsBase,
55 55
 
56 56
     def test_public_execution(self):
57 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()

+ 2
- 2
tox.ini View File

@@ -32,10 +32,10 @@ commands = flake8
32 32
 commands = {posargs}
33 33
 
34 34
 [testenv:py35-integration]
35
-commands = pytest --tb=long --capture=sys --cov=picasso --capture=fd {toxinidir}/picasso/tests/integration
35
+commands = pytest -v --tb=long --capture=sys --cov=picasso --capture=fd {toxinidir}/picasso/tests/integration
36 36
 
37 37
 [testenv:py35-functional]
38
-commands = pytest --tb=long --capture=sys --cov=picasso --capture=fd {toxinidir}/picasso/tests/functional
38
+commands = pytest -v --tb=long --capture=sys --cov=picasso --capture=fd {toxinidir}/picasso/tests/functional
39 39
 
40 40
 [testenv:py35-functional-regression]
41 41
 commands = {toxinidir}/scripts/test_regression.sh functional {posargs}

Loading…
Cancel
Save