apiWriteConfigFile("acl", content);
}
-static bool compareAuthorization(YaHTTP::Request& req, const string &expected_password, const string& expectedApiKey)
+static bool checkAPIKey(const YaHTTP::Request& req, const string& expectedApiKey)
{
- // validate password
- YaHTTP::strstr_map_t::iterator header = req.headers.find("authorization");
- bool auth_ok = false;
- if (header != req.headers.end() && toLower(header->second).find("basic ") == 0) {
- string cookie = header->second.substr(6);
+ if (expectedApiKey.empty()) {
+ return false;
+ }
+
+ const auto header = req.headers.find("x-api-key");
+ if (header != req.headers.end()) {
+ return (header->second == expectedApiKey);
+ }
+
+ return false;
+}
+
+static bool checkWebPassword(const YaHTTP::Request& req, const string &expected_password)
+{
+ static const char basicStr[] = "basic ";
+
+ const auto header = req.headers.find("authorization");
+
+ if (header != req.headers.end() && toLower(header->second).find(basicStr) == 0) {
+ string cookie = header->second.substr(sizeof(basicStr) - 1);
string plain;
B64Decode(cookie, plain);
vector<string> cparts;
stringtok(cparts, plain, ":");
- // this gets rid of terminating zeros
- auth_ok = (cparts.size()==2 && (0==strcmp(cparts[1].c_str(), expected_password.c_str())));
+ if (cparts.size() == 2) {
+ return cparts[1] == expected_password;
+ }
}
- if (!auth_ok && !expectedApiKey.empty()) {
- /* if this is a request for the API,
- check if the API key is correct */
- if (req.url.path=="/jsonstat" ||
- req.url.path.find("/api/") == 0) {
- header = req.headers.find("x-api-key");
- if (header != req.headers.end()) {
- auth_ok = (0==strcmp(header->second.c_str(), expectedApiKey.c_str()));
- }
+
+ return false;
+}
+
+static bool isAnAPIRequest(const YaHTTP::Request& req)
+{
+ return req.url.path.find("/api/") == 0;
+}
+
+static bool isAnAPIRequestAllowedWithWebAuth(const YaHTTP::Request& req)
+{
+ return req.url.path == "/api/v1/servers/localhost";
+}
+
+static bool isAStatsRequest(const YaHTTP::Request& req)
+{
+ return req.url.path == "/jsonstat";
+}
+
+static bool compareAuthorization(const YaHTTP::Request& req, const string &expected_password, const string& expectedApiKey)
+{
+ if (isAnAPIRequest(req)) {
+ /* Access to the API requires a valid API key */
+ if (checkAPIKey(req, expectedApiKey)) {
+ return true;
}
+
+ return isAnAPIRequestAllowedWithWebAuth(req) && checkWebPassword(req, expected_password);
+ }
+
+ if (isAStatsRequest(req)) {
+ /* Access to the stats is allowed for both API and Web users */
+ return checkAPIKey(req, expectedApiKey) || checkWebPassword(req, expected_password);
}
- return auth_ok;
+
+ return checkWebPassword(req, expected_password);
}
static bool isMethodAllowed(const YaHTTP::Request& req)
return false;
}
-static void handleCORS(YaHTTP::Request& req, YaHTTP::Response& resp)
+static void handleCORS(const YaHTTP::Request& req, YaHTTP::Response& resp)
{
- YaHTTP::strstr_map_t::iterator origin = req.headers.find("Origin");
+ const auto origin = req.headers.find("Origin");
if (origin != req.headers.end()) {
if (req.method == "OPTIONS") {
/* Pre-flight request */
}
resp.headers["Access-Control-Allow-Origin"] = origin->second;
- resp.headers["Access-Control-Allow-Credentials"] = "true";
+
+ if (isAStatsRequest(req) || isAnAPIRequestAllowedWithWebAuth(req)) {
+ resp.headers["Access-Control-Allow-Credentials"] = "true";
+ }
}
}
_webServerPort = 8083
_webServerBasicAuthPassword = 'secret'
_webServerAPIKey = 'apisecret'
- # paths accessible using the API key
- _apiPaths = ['/api/v1/servers/localhost', '/api/v1/servers/localhost/config', '/api/v1/servers/localhost/config/allow-from', '/api/v1/servers/localhost/statistics', '/jsonstat?command=stats', '/jsonstat?command=dynblocklist']
+ # paths accessible using the API key only
+ _apiOnlyPaths = ['/api/v1/servers/localhost/config', '/api/v1/servers/localhost/config/allow-from', '/api/v1/servers/localhost/statistics']
+ # paths accessible using an API key or basic auth
+ _statsPaths = [ '/jsonstat?command=stats', '/jsonstat?command=dynblocklist', '/api/v1/servers/localhost']
# paths accessible using basic auth only (list not exhaustive)
_basicOnlyPaths = ['/', '/index.html']
_config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
"""
API: Basic Authentication
"""
- for path in self._basicOnlyPaths + self._apiPaths:
+ for path in self._basicOnlyPaths + self._statsPaths:
url = 'http://127.0.0.1:' + str(self._webServerPort) + path
r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
self.assertTrue(r)
API: X-Api-Key
"""
headers = {'x-api-key': self._webServerAPIKey}
- for path in self._apiPaths:
+ for path in self._apiOnlyPaths + self._statsPaths:
url = 'http://127.0.0.1:' + str(self._webServerPort) + path
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertTrue(r)
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertEquals(r.status_code, 401)
+ def testAPIKeyOnly(self):
+ """
+ API: API Key Only
+ """
+ for path in self._apiOnlyPaths:
+ url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+ r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ self.assertEquals(r.status_code, 401)
+
def testServersLocalhost(self):
"""
API: /api/v1/servers/localhost