mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-13 04:14:42 +00:00
patch: key
This commit is contained in:
1 parent
249dd9ce37
commit
6b6495f48c
3 files changed
+67
-15
No files matched your search
@@ -26,12 +26,20 @@ internal = Blueprint("internal", __name__)
|
||||
|
||||
@internal.before_request
|
||||
def verify_internal_key():
|
||||
"""Verify INTERNAL_KEY for all internal endpoint requests."""
|
||||
if settings.INTERNAL_KEY:
|
||||
internal_key = request.headers.get("X-Internal-Key")
|
||||
if not internal_key or internal_key != settings.INTERNAL_KEY:
|
||||
logger.warning(f"Unauthorized internal API access attempt from {request.remote_addr}")
|
||||
return jsonify({"error": "Unauthorized", "message": "Invalid or missing internal key"}), 401
|
||||
"""Verify INTERNAL_KEY for all internal endpoint requests.
|
||||
|
||||
Deny by default: if INTERNAL_KEY is not configured, reject all requests.
|
||||
"""
|
||||
if not settings.INTERNAL_KEY:
|
||||
logger.warning(
|
||||
f"Internal API request rejected from {request.remote_addr}: "
|
||||
"INTERNAL_KEY is not configured"
|
||||
)
|
||||
return jsonify({"error": "Unauthorized", "message": "Internal API is not configured"}), 401
|
||||
internal_key = request.headers.get("X-Internal-Key")
|
||||
if not internal_key or internal_key != settings.INTERNAL_KEY:
|
||||
logger.warning(f"Unauthorized internal API access attempt from {request.remote_addr}")
|
||||
return jsonify({"error": "Unauthorized", "message": "Invalid or missing internal key"}), 401
|
||||
|
||||
|
||||
@internal.route("/api/download", methods=["get"])
|
||||
|
||||
@@ -48,7 +48,7 @@ def internal_app(monkeypatch, mock_mongo_db):
|
||||
@pytest.mark.unit
|
||||
class TestVerifyInternalKey:
|
||||
|
||||
def test_no_internal_key_configured_allows_access(
|
||||
def test_no_internal_key_configured_rejects_access(
|
||||
self, internal_app, monkeypatch
|
||||
):
|
||||
app, db = internal_app
|
||||
@@ -63,9 +63,8 @@ class TestVerifyInternalKey:
|
||||
),
|
||||
)
|
||||
with app.test_client() as client:
|
||||
# download will fail for missing file but should not be 401
|
||||
resp = client.get("/api/download?user=u&name=n&file=f")
|
||||
assert resp.status_code != 401
|
||||
assert resp.status_code == 401
|
||||
|
||||
def test_missing_key_returns_401(self, internal_app, monkeypatch):
|
||||
app, db = internal_app
|
||||
@@ -131,9 +130,12 @@ class TestVerifyInternalKey:
|
||||
@pytest.mark.unit
|
||||
class TestUploadIndex:
|
||||
|
||||
_TEST_INTERNAL_KEY = "test-internal-key"
|
||||
_AUTH_HEADERS = {"X-Internal-Key": "test-internal-key"}
|
||||
|
||||
def _make_settings(self, vector_store="faiss"):
|
||||
return MagicMock(
|
||||
INTERNAL_KEY=None,
|
||||
INTERNAL_KEY=self._TEST_INTERNAL_KEY,
|
||||
UPLOAD_FOLDER="uploads",
|
||||
VECTOR_STORE=vector_store,
|
||||
EMBEDDINGS_NAME="test_embeddings",
|
||||
@@ -146,7 +148,7 @@ class TestUploadIndex:
|
||||
"application.api.internal.routes.settings", self._make_settings()
|
||||
)
|
||||
with app.test_client() as client:
|
||||
resp = client.post("/api/upload_index", data={})
|
||||
resp = client.post("/api/upload_index", data={}, headers=self._AUTH_HEADERS)
|
||||
assert resp.json["status"] == "no user"
|
||||
|
||||
def test_missing_name_returns_no_name(self, internal_app, monkeypatch):
|
||||
@@ -155,7 +157,7 @@ class TestUploadIndex:
|
||||
"application.api.internal.routes.settings", self._make_settings()
|
||||
)
|
||||
with app.test_client() as client:
|
||||
resp = client.post("/api/upload_index", data={"user": "testuser"})
|
||||
resp = client.post("/api/upload_index", data={"user": "testuser"}, headers=self._AUTH_HEADERS)
|
||||
assert resp.json["status"] == "no name"
|
||||
|
||||
def test_creates_new_source_entry(self, internal_app, monkeypatch):
|
||||
@@ -182,6 +184,7 @@ class TestUploadIndex:
|
||||
"id": doc_id,
|
||||
"type": "local",
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
@@ -219,6 +222,7 @@ class TestUploadIndex:
|
||||
"id": str(doc_id),
|
||||
"type": "remote",
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
@@ -252,6 +256,7 @@ class TestUploadIndex:
|
||||
"type": "local",
|
||||
"directory_structure": json.dumps(dir_struct),
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
@@ -285,6 +290,7 @@ class TestUploadIndex:
|
||||
"type": "local",
|
||||
"directory_structure": "not valid json",
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
@@ -317,6 +323,7 @@ class TestUploadIndex:
|
||||
"type": "local",
|
||||
"file_name_map": json.dumps(fmap),
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
@@ -349,6 +356,7 @@ class TestUploadIndex:
|
||||
"id": doc_id,
|
||||
"type": "local",
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "no file"
|
||||
|
||||
@@ -379,6 +387,7 @@ class TestUploadIndex:
|
||||
"type": "local",
|
||||
"file_faiss": (io.BytesIO(b""), ""),
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "no file name"
|
||||
|
||||
@@ -408,6 +417,7 @@ class TestUploadIndex:
|
||||
"remote_data": '{"url":"http://example.com"}',
|
||||
"sync_frequency": "daily",
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
@@ -443,6 +453,7 @@ class TestUploadIndex:
|
||||
"file_pkl": (io.BytesIO(b"pkl data"), "index.pkl"),
|
||||
},
|
||||
content_type="multipart/form-data",
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
@@ -477,6 +488,7 @@ class TestUploadIndex:
|
||||
"file_faiss": (io.BytesIO(b"faiss data"), "index.faiss"),
|
||||
},
|
||||
content_type="multipart/form-data",
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "no file"
|
||||
|
||||
@@ -508,9 +520,27 @@ class TestUploadIndex:
|
||||
"file_pkl": (io.BytesIO(b""), ""),
|
||||
},
|
||||
content_type="multipart/form-data",
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "no file name"
|
||||
|
||||
def test_no_internal_key_rejects_upload(self, internal_app, monkeypatch):
|
||||
"""Verify that upload_index is rejected when INTERNAL_KEY is not set."""
|
||||
app, db = internal_app
|
||||
monkeypatch.setattr(
|
||||
"application.api.internal.routes.settings",
|
||||
MagicMock(
|
||||
INTERNAL_KEY=None,
|
||||
UPLOAD_FOLDER="uploads",
|
||||
VECTOR_STORE="faiss",
|
||||
EMBEDDINGS_NAME="test",
|
||||
MONGO_DB_NAME="docsgpt",
|
||||
),
|
||||
)
|
||||
with app.test_client() as client:
|
||||
resp = client.post("/api/upload_index", data={"user": "attacker"})
|
||||
assert resp.status_code == 401
|
||||
|
||||
def test_update_existing_with_file_name_map(self, internal_app, monkeypatch):
|
||||
"""Cover line 124: update existing entry with file_name_map."""
|
||||
app, db = internal_app
|
||||
@@ -540,6 +570,7 @@ class TestUploadIndex:
|
||||
"type": "local",
|
||||
"file_name_map": json.dumps(fmap),
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
@@ -572,6 +603,7 @@ class TestUploadIndex:
|
||||
"type": "local",
|
||||
"file_name_map": "not valid json{{{",
|
||||
},
|
||||
headers=self._AUTH_HEADERS,
|
||||
)
|
||||
assert resp.json["status"] == "ok"
|
||||
|
||||
|
||||
@@ -2105,23 +2105,35 @@ class TestInternalRoutes:
|
||||
app.register_blueprint(internal)
|
||||
return app
|
||||
|
||||
_TEST_KEY = "test-key"
|
||||
_AUTH_HEADERS = {"X-Internal-Key": "test-key"}
|
||||
|
||||
def test_upload_index_no_user(self, app):
|
||||
with app.test_client() as client:
|
||||
with patch(
|
||||
"application.api.internal.routes.settings"
|
||||
) as ms:
|
||||
ms.INTERNAL_KEY = None
|
||||
resp = client.post("/api/upload_index")
|
||||
ms.INTERNAL_KEY = self._TEST_KEY
|
||||
resp = client.post("/api/upload_index", headers=self._AUTH_HEADERS)
|
||||
assert resp.get_json()["status"] == "no user"
|
||||
|
||||
def test_upload_index_no_name(self, app):
|
||||
with app.test_client() as client:
|
||||
with patch(
|
||||
"application.api.internal.routes.settings"
|
||||
) as ms:
|
||||
ms.INTERNAL_KEY = self._TEST_KEY
|
||||
resp = client.post("/api/upload_index", data={"user": "u1"}, headers=self._AUTH_HEADERS)
|
||||
assert resp.get_json()["status"] == "no name"
|
||||
|
||||
def test_upload_index_rejected_without_internal_key(self, app):
|
||||
with app.test_client() as client:
|
||||
with patch(
|
||||
"application.api.internal.routes.settings"
|
||||
) as ms:
|
||||
ms.INTERNAL_KEY = None
|
||||
resp = client.post("/api/upload_index", data={"user": "u1"})
|
||||
assert resp.get_json()["status"] == "no name"
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user