Skip to content

Commit 184078b

Browse files
Merge pull request #31 from DataKitchen/release/2.7.3
Release/2.7.3
2 parents fedfa3d + fcf85e8 commit 184078b

File tree

21 files changed

+1359
-1168
lines changed

21 files changed

+1359
-1168
lines changed

agent_api/app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from agent_api.routes import build_v1_routes
77
from common.api.flask_ext.authentication import ServiceAccountAuth
88
from common.api.flask_ext.config import Config
9+
from common.api.flask_ext.cors import CORS
910
from common.api.flask_ext.database_connection import DatabaseConnection
1011
from common.api.flask_ext.exception_handling import ExceptionHandling
1112
from common.api.flask_ext.health import Health
@@ -15,6 +16,7 @@
1516
# Create and configure the app
1617
app = Flask(__name__, instance_relative_config=True)
1718
Config(app, config_module="agent_api.config")
19+
CORS(app, allowed_methods=["POST"])
1820
Logging(app)
1921
ExceptionHandling(app)
2022
Timing(app)

common/api/flask_ext/cors.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
__all__ = ["CORS"]
2+
from conf import settings
23
from http import HTTPStatus
4+
from fnmatch import fnmatch
35
from typing import Optional
6+
from urllib.parse import urlparse
47

58
from flask import Flask, Response, make_response, request
69
from werkzeug.exceptions import NotFound
710

811
from common.api.flask_ext.base_extension import BaseExtension
912

10-
# TODO: See PD-286 -- at some point we should not just use wildcards and actually settle on a strategy of indicating
11-
# which domains to accept cross-origin-resource-sharing from.
1213
CORS_HEADERS: dict[str, str] = {
13-
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
14-
"Access-Control-Allow-Origin": "*",
1514
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
16-
"Access-Control-Allow-Headers": "*",
17-
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
18-
"Access-Control-Allow-Methods": "*",
15+
"Access-Control-Allow-Headers": "Accept, Content-Type, Origin, Host, Authorization, X-Forwarded-For",
1916
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
20-
"Access-Control-Expose-Headers": "*",
17+
# Add this only if we want to expose custom response headers
18+
# "Access-Control-Expose-Headers": "*",
2119
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
2220
"Access-Control-Allow-Credentials": "true",
2321
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
@@ -43,12 +41,21 @@ def make_preflight_response() -> Optional[Response]:
4341
else:
4442
return None
4543

46-
@staticmethod
47-
def set_cors_headers(response: Response) -> Response:
48-
if response.headers is not None:
49-
response.headers.update(CORS_HEADERS)
50-
return response
44+
def set_cors_headers(self, response: Response) -> Response:
45+
if response.headers is not None and (origin := request.headers.get("Origin")) is not None:
46+
try:
47+
netloc = urlparse(origin).netloc
48+
if any([fnmatch(netloc, pattern) for pattern in settings.CORS_DOMAINS]):
49+
response.headers.update(CORS_HEADERS)
50+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
51+
response.headers["Access-Control-Allow-Origin"] = origin
52+
response.headers["Vary"] = "Origin"
53+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
54+
response.headers["Access-Control-Allow-Methods"] = self.allowed_methods
55+
except Exception:
56+
pass
57+
return response
5158

5259
def init_app(self) -> None:
5360
self.add_before_request_func(CORS.make_preflight_response)
54-
self.add_after_request_func(CORS.set_cors_headers)
61+
self.add_after_request_func(self.set_cors_headers)

common/tests/integration/flask_ext/test_cors.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def client(flask_app):
2323
@pytest.mark.integration
2424
def test_options_request(client):
2525
"""OPTIONS request intercepted and given NO_CONTENT repsonse + CORS headers."""
26-
response = client.options("/test-endpoint")
26+
response = client.options("/test-endpoint", headers={"Origin": "https://xyz.com"})
2727
assert response.status_code == HTTPStatus.NO_CONTENT
2828
for header_name in CORS_HEADER_TUPLE:
2929
assert header_name in response.headers
@@ -32,7 +32,7 @@ def test_options_request(client):
3232
@pytest.mark.integration
3333
def test_options_request_404(client):
3434
"""OPTIONS request returns a 404 for invalid url endpoints."""
35-
response = client.options("/bad-endpoint")
35+
response = client.options("/bad-endpoint", headers={"Origin": "https://xyz.com"})
3636
assert response.status_code == HTTPStatus.NOT_FOUND
3737

3838

@@ -42,7 +42,7 @@ def test_options_request_404(client):
4242
@pytest.mark.integration
4343
def test_requests_get(client):
4444
"""Served GET requests have proper headers."""
45-
response = client.get("/test-endpoint")
45+
response = client.get("/test-endpoint", headers={"Origin": "https://xyz.com"})
4646
assert response.status_code == HTTPStatus.OK
4747
for header_name in CORS_HEADER_TUPLE:
4848
assert header_name in response.headers
@@ -51,7 +51,7 @@ def test_requests_get(client):
5151
@pytest.mark.integration
5252
def test_requests_put(client):
5353
"""Served PUT requests have proper headers."""
54-
response = client.put("/test-endpoint")
54+
response = client.put("/test-endpoint", headers={"Origin": "https://xyz.com"})
5555
assert response.status_code == HTTPStatus.OK
5656
for header_name in CORS_HEADER_TUPLE:
5757
assert header_name in response.headers
@@ -60,7 +60,7 @@ def test_requests_put(client):
6060
@pytest.mark.integration
6161
def test_requests_post(client):
6262
"""Served POST requests have proper headers."""
63-
response = client.post("/test-endpoint")
63+
response = client.post("/test-endpoint", headers={"Origin": "https://xyz.com"})
6464
assert response.status_code == HTTPStatus.OK
6565
for header_name in CORS_HEADER_TUPLE:
6666
assert header_name in response.headers
@@ -69,7 +69,7 @@ def test_requests_post(client):
6969
@pytest.mark.integration
7070
def test_requests_patch(client):
7171
"""Served PATCH requests have proper headers."""
72-
response = client.patch("/test-endpoint")
72+
response = client.patch("/test-endpoint", headers={"Origin": "https://xyz.com"})
7373
assert response.status_code == HTTPStatus.OK
7474
for header_name in CORS_HEADER_TUPLE:
7575
assert header_name in response.headers
@@ -78,7 +78,7 @@ def test_requests_patch(client):
7878
@pytest.mark.integration
7979
def test_requests_delete(client):
8080
"""Served DELETE requests have proper headers."""
81-
response = client.delete("/test-endpoint")
81+
response = client.delete("/test-endpoint", headers={"Origin": "https://xyz.com"})
8282
assert response.status_code == HTTPStatus.NO_CONTENT
8383
for header_name in CORS_HEADER_TUPLE:
8484
assert header_name in response.headers
Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import flask
12
import pytest
23
from flask import Response
34

@@ -7,8 +8,30 @@
78

89

910
@pytest.mark.unit
10-
def test_headers_added():
11-
response = Response()
12-
CORS.set_cors_headers(response)
13-
for header_name in CORS_HEADER_TUPLE:
14-
assert header_name in response.headers
11+
def test_headers_added_matching_domain():
12+
test_app = flask.Flask("test_flask_app")
13+
cors = CORS()
14+
15+
matching_domains = ["https://xyz.com", "https://test.mydomain.com", "http://localhost:8080"]
16+
for domain in matching_domains:
17+
with test_app.test_request_context(headers={"Origin": domain}):
18+
response = Response()
19+
cors.set_cors_headers(response)
20+
assert response.headers["Access-Control-Allow-Origin"] is domain
21+
for header_name in CORS_HEADER_TUPLE:
22+
assert header_name in response.headers
23+
24+
25+
@pytest.mark.unit
26+
def test_no_headers_other_domain():
27+
test_app = flask.Flask("test_flask_app")
28+
cors = CORS()
29+
30+
matching_domains = ["https://abc.com", "https://test.mydomain.com.io", "http://localhost"]
31+
for domain in matching_domains:
32+
with test_app.test_request_context(headers={"Origin": domain}):
33+
response = Response()
34+
cors.set_cors_headers(response)
35+
assert "Access-Control-Allow-Origin" not in response.headers
36+
for header_name in CORS_HEADER_TUPLE:
37+
assert header_name not in response.headers

conf/cloud.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ class ReconnectingPooledMySQLDatabase(ReconnectMixin, PooledMySQLDatabase):
99
pass
1010

1111

12+
CORS_DOMAINS: list[str] = os.environ.get("CORS_DOMAINS", "*").split(",")
13+
1214
DATABASE: dict[str, object] = {
1315
"name": "datakitchen",
1416
"engine": ReconnectingPooledMySQLDatabase,

conf/minikube.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ class ReconnectingPooledMySQLDatabase(ReconnectMixin, PooledMySQLDatabase):
88
pass
99

1010

11+
CORS_DOMAINS: list[str] = os.environ.get("CORS_DOMAINS", "*").split(",")
12+
1113
DATABASE: dict[str, object] = {
1214
"name": "datakitchen",
1315
"engine": ReconnectingPooledMySQLDatabase,

conf/test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from peewee import SqliteDatabase
22

3+
CORS_DOMAINS: list[str] = ["*.mydomain.com", "xyz.com", "localhost:*"]
4+
35
DATABASE: dict[str, object] = {
46
"name": "file:cachedb?mode=memory&cache=shared",
57
"engine": SqliteDatabase,

deploy/charts/observability-app/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: v2
22
name: dataops-observability-app
33
type: application
44
appVersion: "2.x.x"
5-
version: "2.2.4"
5+
version: "2.3.0"
66

77
description: DataOps Observability
88
home: https://datakitchen.io

deploy/charts/observability-app/templates/_environments.tpl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Environment
1515
{{- define "observability.environment.flask" -}}
1616
- name: FLASK_DEBUG
1717
value: {{ .Values.observability.flask_debug | quote }}
18+
- name: CORS_DOMAINS
19+
value: {{ .Values.observability.cors_domains | quote }}
1820
{{- end -}}
1921

2022
{{- define "observability.environment.database" -}}

deploy/charts/observability-app/templates/_helpers.tpl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,10 @@ CLI Hook
187187
{{- define "observability.cli_hook.image" }}
188188
{{- include "observability.image" (list . .Values.cli_hook) }}
189189
{{- end }}
190+
191+
{{/*
192+
Cronjob
193+
*/}}
194+
{{- define "observability.cronjob.name" -}}
195+
cronjob-{{ .name }}
196+
{{- end -}}

0 commit comments

Comments
 (0)