Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 7f89295

Browse files
fix: add Authorization Bearer (#2062)
* fix: add simple authentication * fix: exclude v1/configs --------- Co-authored-by: sangjanai <[email protected]>
1 parent db3a05a commit 7f89295

File tree

6 files changed

+99
-13
lines changed

6 files changed

+99
-13
lines changed

engine/common/api_server_configuration.h

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class ApiServerConfiguration {
107107
const std::string& proxy_url = "", const std::string& proxy_username = "",
108108
const std::string& proxy_password = "", const std::string& no_proxy = "",
109109
bool verify_peer_ssl = true, bool verify_host_ssl = true,
110-
const std::string& hf_token = "")
110+
const std::string& hf_token = "", std::vector<std::string> api_keys = {})
111111
: cors{cors},
112112
allowed_origins{allowed_origins},
113113
verify_proxy_ssl{verify_proxy_ssl},
@@ -118,7 +118,8 @@ class ApiServerConfiguration {
118118
no_proxy{no_proxy},
119119
verify_peer_ssl{verify_peer_ssl},
120120
verify_host_ssl{verify_host_ssl},
121-
hf_token{hf_token} {}
121+
hf_token{hf_token},
122+
api_keys{api_keys} {}
122123

123124
// cors
124125
bool cors{true};
@@ -139,6 +140,9 @@ class ApiServerConfiguration {
139140
// token
140141
std::string hf_token{""};
141142

143+
// authentication
144+
std::vector<std::string> api_keys;
145+
142146
Json::Value ToJson() const {
143147
Json::Value root;
144148
root["cors"] = cors;
@@ -155,6 +159,10 @@ class ApiServerConfiguration {
155159
root["verify_peer_ssl"] = verify_peer_ssl;
156160
root["verify_host_ssl"] = verify_host_ssl;
157161
root["huggingface_token"] = hf_token;
162+
root["api_keys"] = Json::Value(Json::arrayValue);
163+
for (const auto& api_key : api_keys) {
164+
root["api_keys"].append(api_key);
165+
}
158166

159167
return root;
160168
}
@@ -256,7 +264,8 @@ class ApiServerConfiguration {
256264
return true;
257265
}},
258266

259-
{"allowed_origins", [this](const Json::Value& value) -> bool {
267+
{"allowed_origins",
268+
[this](const Json::Value& value) -> bool {
260269
if (!value.isArray()) {
261270
return false;
262271
}
@@ -271,7 +280,26 @@ class ApiServerConfiguration {
271280
this->allowed_origins.push_back(origin.asString());
272281
}
273282
return true;
274-
}}};
283+
}},
284+
285+
{"api_keys",
286+
[this](const Json::Value& value) -> bool {
287+
if (!value.isArray()) {
288+
return false;
289+
}
290+
for (const auto& key : value) {
291+
if (!key.isString()) {
292+
return false;
293+
}
294+
}
295+
296+
this->api_keys.clear();
297+
for (const auto& key : value) {
298+
this->api_keys.push_back(key.asString());
299+
}
300+
return true;
301+
}},
302+
};
275303

276304
for (const auto& key : json.getMemberNames()) {
277305
auto updater = field_updater.find(key);

engine/main.cc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,55 @@ void RunServer(std::optional<std::string> host, std::optional<int> port,
250250
.setClientMaxBodySize(256 * 1024 * 1024) // Max 256MiB body size
251251
.setClientMaxMemoryBodySize(1024 * 1024); // 1MiB before writing to disk
252252

253+
auto validate_api_key = [config_service](const drogon::HttpRequestPtr& req) {
254+
auto const& api_keys =
255+
config_service->GetApiServerConfiguration()->api_keys;
256+
static const std::unordered_set<std::string> public_endpoints = {
257+
"/healthz", "/processManager/destroy"};
258+
259+
// If API key is not set, skip validation
260+
if (api_keys.empty()) {
261+
return true;
262+
}
263+
264+
// If path is public or is static file, skip validation
265+
if (public_endpoints.find(req->path()) != public_endpoints.end() ||
266+
req->path() == "/") {
267+
return true;
268+
}
269+
270+
// Check for API key in the header
271+
auto auth_header = req->getHeader("Authorization");
272+
273+
std::string prefix = "Bearer ";
274+
if (auth_header.substr(0, prefix.size()) == prefix) {
275+
std::string received_api_key = auth_header.substr(prefix.size());
276+
if (std::find(api_keys.begin(), api_keys.end(), received_api_key) !=
277+
api_keys.end()) {
278+
return true; // API key is valid
279+
}
280+
}
281+
282+
CTL_WRN("Unauthorized: Invalid API Key\n");
283+
return false;
284+
};
285+
286+
drogon::app().registerPreRoutingAdvice(
287+
[&validate_api_key](
288+
const drogon::HttpRequestPtr& req,
289+
std::function<void(const drogon::HttpResponsePtr&)>&& cb,
290+
drogon::AdviceChainCallback&& ccb) {
291+
if (!validate_api_key(req)) {
292+
Json::Value ret;
293+
ret["message"] = "Invalid API Key";
294+
auto resp = cortex_utils::CreateCortexHttpJsonResponse(ret);
295+
resp->setStatusCode(drogon::k401Unauthorized);
296+
cb(resp);
297+
return;
298+
}
299+
ccb();
300+
});
301+
253302
// CORS
254303
drogon::app().registerPostHandlingAdvice(
255304
[config_service](const drogon::HttpRequestPtr& req,

engine/services/config_service.cc

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ cpp::result<ApiServerConfiguration, std::string>
66
ConfigService::UpdateApiServerConfiguration(const Json::Value& json) {
77
auto config = file_manager_utils::GetCortexConfig();
88
ApiServerConfiguration api_server_config{
9-
config.enableCors, config.allowedOrigins, config.verifyProxySsl,
10-
config.verifyProxyHostSsl, config.proxyUrl, config.proxyUsername,
11-
config.proxyPassword, config.noProxy, config.verifyPeerSsl,
12-
config.verifyHostSsl, config.huggingFaceToken};
9+
config.enableCors, config.allowedOrigins, config.verifyProxySsl,
10+
config.verifyProxyHostSsl, config.proxyUrl, config.proxyUsername,
11+
config.proxyPassword, config.noProxy, config.verifyPeerSsl,
12+
config.verifyHostSsl, config.huggingFaceToken, config.apiKeys};
1313

1414
std::vector<std::string> updated_fields;
1515
std::vector<std::string> invalid_fields;
@@ -36,6 +36,7 @@ ConfigService::UpdateApiServerConfiguration(const Json::Value& json) {
3636
config.verifyHostSsl = api_server_config.verify_host_ssl;
3737

3838
config.huggingFaceToken = api_server_config.hf_token;
39+
config.apiKeys = api_server_config.api_keys;
3940

4041
auto result = file_manager_utils::UpdateCortexConfig(config);
4142
return api_server_config;
@@ -45,8 +46,8 @@ cpp::result<ApiServerConfiguration, std::string>
4546
ConfigService::GetApiServerConfiguration() {
4647
auto config = file_manager_utils::GetCortexConfig();
4748
return ApiServerConfiguration{
48-
config.enableCors, config.allowedOrigins, config.verifyProxySsl,
49-
config.verifyProxyHostSsl, config.proxyUrl, config.proxyUsername,
50-
config.proxyPassword, config.noProxy, config.verifyPeerSsl,
51-
config.verifyHostSsl, config.huggingFaceToken};
49+
config.enableCors, config.allowedOrigins, config.verifyProxySsl,
50+
config.verifyProxyHostSsl, config.proxyUrl, config.proxyUsername,
51+
config.proxyPassword, config.noProxy, config.verifyPeerSsl,
52+
config.verifyHostSsl, config.huggingFaceToken, config.apiKeys};
5253
}

engine/utils/config_yaml_utils.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ cpp::result<void, std::string> CortexConfigMgr::DumpYamlConfig(
5151
node["sslKeyPath"] = config.sslKeyPath;
5252
node["supportedEngines"] = config.supportedEngines;
5353
node["checkedForSyncHubAt"] = config.checkedForSyncHubAt;
54+
node["apiKeys"] = config.apiKeys;
5455

5556
out_file << node;
5657
out_file.close();
@@ -87,7 +88,7 @@ CortexConfig CortexConfigMgr::FromYaml(const std::string& path,
8788
!node["verifyProxySsl"] || !node["verifyProxyHostSsl"] ||
8889
!node["supportedEngines"] || !node["sslCertPath"] ||
8990
!node["sslKeyPath"] || !node["noProxy"] ||
90-
!node["checkedForSyncHubAt"]);
91+
!node["checkedForSyncHubAt"] || !node["apiKeys"]);
9192

9293
CortexConfig config = {
9394
.logFolderPath = node["logFolderPath"]
@@ -182,6 +183,11 @@ CortexConfig CortexConfigMgr::FromYaml(const std::string& path,
182183
.checkedForSyncHubAt = node["checkedForSyncHubAt"]
183184
? node["checkedForSyncHubAt"].as<uint64_t>()
184185
: default_cfg.checkedForSyncHubAt,
186+
.apiKeys =
187+
node["apiKeys"]
188+
? node["apiKeys"].as<std::vector<std::string>>()
189+
: default_cfg.apiKeys,
190+
185191
};
186192
if (should_update_config) {
187193
l.unlock();

engine/utils/config_yaml_utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct CortexConfig {
6868
std::string sslKeyPath;
6969
std::vector<std::string> supportedEngines;
7070
uint64_t checkedForSyncHubAt;
71+
std::vector<std::string> apiKeys;
7172
};
7273

7374
class CortexConfigMgr {

engine/utils/file_manager_utils.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ config_yaml_utils::CortexConfig GetDefaultConfig() {
195195
.sslKeyPath = "",
196196
.supportedEngines = config_yaml_utils::kDefaultSupportedEngines,
197197
.checkedForSyncHubAt = 0u,
198+
.apiKeys = {},
198199
};
199200
}
200201

0 commit comments

Comments
 (0)