Skip to content

Commit 88e07d4

Browse files
author
Gabriel Monroy
committed
feat(builder): expose runtime configuration during slugbuilder execution
This uses a new Config API Hook to grab the latest app configuration and expose it to slugbuilder using `-e` flags. While this works, it is a very good mechanism for handling user-defined inputs on the builder. In other words, command-injection is possible through malformed inputs. We must consider this as we move to refactor the builder in Go.
1 parent 4f58653 commit 88e07d4

3 files changed

Lines changed: 52 additions & 0 deletions

File tree

api/tests/test_hooks.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,31 @@ def test_build_hook(self):
118118
self.assertIn('release', response.data)
119119
self.assertIn('version', response.data['release'])
120120
self.assertIn('domains', response.data)
121+
122+
def test_config_hook(self):
123+
"""Test creating a Config via an API Hook"""
124+
url = '/api/apps'
125+
body = {'cluster': 'autotest'}
126+
response = self.client.post(url, json.dumps(body), content_type='application/json')
127+
self.assertEqual(response.status_code, 201)
128+
app_id = response.data['id']
129+
url = '/api/apps/{app_id}/config'.format(**locals())
130+
response = self.client.get(url)
131+
self.assertEqual(response.status_code, 200)
132+
self.assertIn('values', response.data)
133+
values = response.data['values']
134+
# prepare the config hook
135+
config = {'username': 'autotest', 'app': app_id}
136+
url = '/api/hooks/config'.format(**locals())
137+
body = {'receive_user': 'autotest',
138+
'receive_repo': app_id}
139+
# post without a session
140+
self.assertIsNone(self.client.logout())
141+
response = self.client.post(url, json.dumps(body), content_type='application/json')
142+
self.assertEqual(response.status_code, 403)
143+
# post with the builder auth key
144+
response = self.client.post(url, json.dumps(body), content_type='application/json',
145+
HTTP_X_DEIS_BUILDER_AUTH=settings.BUILDER_KEY)
146+
self.assertEqual(response.status_code, 200)
147+
self.assertIn('values', response.data)
148+
self.assertEqual(values, response.data['values'])

api/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@
165165
166166
Create a new :class:`~api.models.Build`.
167167
168+
.. http:post:: /api/hooks/config/
169+
170+
Retrieve latest application :class:`~api.models.Config`.
171+
168172
169173
Auth
170174
====
@@ -279,6 +283,8 @@
279283
views.PushHookViewSet.as_view({'post': 'create'})),
280284
url(r'^hooks/build/?',
281285
views.BuildHookViewSet.as_view({'post': 'create'})),
286+
url(r'^hooks/config/?',
287+
views.ConfigHookViewSet.as_view({'post': 'create'})),
282288
# authn / authz
283289
url(r'^auth/register/?',
284290
views.UserRegistrationView.as_view({'post': 'create'})),

api/views.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,3 +512,21 @@ def post_save(self, build, created=False):
512512
release = build.app.release_set.latest()
513513
new_release = release.new(build.owner, build=build)
514514
build.app.deploy(new_release)
515+
516+
517+
class ConfigHookViewSet(BaseHookViewSet):
518+
"""API hook to grab latest :class:`~api.models.Config`"""
519+
520+
model = models.Config
521+
serializer_class = serializers.ConfigSerializer
522+
523+
def create(self, request, *args, **kwargs):
524+
app = get_object_or_404(models.App, id=request.DATA['receive_repo'])
525+
user = get_object_or_404(
526+
User, username=request.DATA['receive_user'])
527+
# check the user is authorized for this app
528+
if user == app.owner or user in get_users_with_perms(app):
529+
config = app.release_set.latest().config
530+
serializer = self.get_serializer(config)
531+
return Response(serializer.data, status=status.HTTP_200_OK)
532+
raise PermissionDenied()

0 commit comments

Comments
 (0)