Blob Blame History Raw
From 6a623dbb64785b882a864b4c80e0f8e052062848 Mon Sep 17 00:00:00 2001
From: Jan Kaluza <jkaluza@redhat.com>
Date: Sep 10 2020 10:59:02 +0000
Subject: Set Access-Control-Allow-Origin header to allow CORS for GET requests.


This is needed to enable Cross-Origin Resource Sharing for ODCS. It
is needed if you want to parse the ODCS API results using javascript
to produce some reports on external sites.

---

diff --git a/server/odcs/server/api_utils.py b/server/odcs/server/api_utils.py
index 5d6e7bc..5ae5467 100644
--- a/server/odcs/server/api_utils.py
+++ b/server/odcs/server/api_utils.py
@@ -22,7 +22,8 @@
 import copy
 import six
 import flask
-from flask import request, url_for
+from flask import request, url_for, Response
+from functools import wraps
 from odcs.server.models import Compose
 from odcs.server import conf
 from odcs.server.errors import Forbidden
@@ -357,3 +358,30 @@ def filter_composes(flask_request):
     page = flask_request.args.get("page", 1, type=int)
     per_page = flask_request.args.get("per_page", 10, type=int)
     return query.paginate(page, per_page, False)
+
+
+def cors_header(allow="*"):
+    """
+    A decorator that sets the Access-Control-Allow-Origin header to
+    the desired value on a Flask route.
+    :param allow: a string of the domain to allow. This defaults to '*'.
+    """
+
+    def decorator(func):
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            rv = func(*args, **kwargs)
+            if rv:
+                # If a tuple was provided, then the Flask Response should be the first object
+                if isinstance(rv, tuple):
+                    response = rv[0]
+                else:
+                    response = rv
+                # Make sure we are dealing with a Flask Response object
+                if isinstance(response, Response):
+                    response.headers.add("Access-Control-Allow-Origin", allow)
+            return rv
+
+        return wrapper
+
+    return decorator
diff --git a/server/odcs/server/views.py b/server/odcs/server/views.py
index 180bfcc..88e110f 100644
--- a/server/odcs/server/views.py
+++ b/server/odcs/server/views.py
@@ -44,6 +44,7 @@ from odcs.server.api_utils import (
     filter_composes,
     validate_json_data,
     raise_if_input_not_allowed,
+    cors_header,
 )
 from odcs.server.auth import requires_role, login_required, has_role
 from odcs.server.auth import require_scopes
@@ -104,6 +105,7 @@ class ODCSAPI(MethodView):
         else:
             return conf.seconds_to_live
 
+    @cors_header()
     def get(self, id):
         """Returns ODCS composes.
 
@@ -592,6 +594,7 @@ class ODCSAPI(MethodView):
 
 
 class AboutAPI(MethodView):
+    @cors_header()
     def get(self):
         """Returns information about this ODCS instance in JSON format.
 
@@ -632,6 +635,7 @@ class Index(View):
 
 
 class MetricsAPI(MethodView):
+    @cors_header()
     def get(self):
         """
         Returns the Prometheus metrics.
diff --git a/server/tests/test_views.py b/server/tests/test_views.py
index a1aaa57..73692b7 100644
--- a/server/tests/test_views.py
+++ b/server/tests/test_views.py
@@ -1361,6 +1361,7 @@ class TestViews(ViewBaseTest):
         self.assertEqual(data["id"], 1)
         self.assertEqual(data["source"], "testmodule:master")
         self.assertEqual(data["pungi_config_dump"], None)
+        self.assertEqual(resp.headers["Access-Control-Allow-Origin"], "*")
 
     def test_query_composes(self):
         resp = self.client.get("/api/1/composes/")