setWebserverStatsRequireAuthentication(boost::get<bool>(vars->at("statsRequireAuthentication")));
}
+ if (vars->count("apiRequiresAuthentication")) {
+ setWebserverAPIRequiresAuthentication(boost::get<bool>(vars->at("apiRequiresAuthentication")));
+ }
+
if (vars->count("maxConcurrentConnections")) {
setWebserverMaxConcurrentConnections(std::stoi(boost::get<std::string>(vars->at("maxConcurrentConnections"))));
}
std::unique_ptr<CredentialsHolder> password;
std::unique_ptr<CredentialsHolder> apiKey;
boost::optional<std::unordered_map<std::string, std::string> > customHeaders;
+ bool apiRequiresAuthentication{true};
bool statsRequireAuthentication{true};
};
else {
out << "None" << endl;
}
+ out << "API requires authentication: " << (config->apiRequiresAuthentication ? "yes" : "no") << endl;
out << "Statistics require authentication: " << (config->statsRequireAuthentication ? "yes" : "no") << endl;
out << "Password: " << (config->password ? "set" : "unset") << endl;
out << "API key: " << (config->apiKey ? "set" : "unset") << endl;
if (isAnAPIRequest(req)) {
/* Access to the API requires a valid API key */
- if (checkAPIKey(req, config->apiKey)) {
+ if (!config->apiRequiresAuthentication || checkAPIKey(req, config->apiKey)) {
return true;
}
g_webserverConfig.lock()->statsRequireAuthentication = require;
}
+void setWebserverAPIRequiresAuthentication(bool require)
+{
+ g_webserverConfig.lock()->apiRequiresAuthentication = require;
+}
+
void setWebserverMaxConcurrentConnections(size_t max)
{
s_connManager.setMaxConcurrentConnections(max);
void setWebserverPassword(std::unique_ptr<CredentialsHolder>&& password);
void setWebserverACL(const std::string& acl);
void setWebserverCustomHeaders(const boost::optional<std::unordered_map<std::string, std::string> > customHeaders);
+void setWebserverAPIRequiresAuthentication(bool);
void setWebserverStatsRequireAuthentication(bool);
void setWebserverMaxConcurrentConnections(size_t);
The optional ``password`` and ``apiKey`` parameters now accept hashed passwords.
The optional ``hashPlaintextCredentials`` parameter has been added.
+ .. versionchanged:: 1.6.0
+ ``apiRequiresAuthentication`` optional parameters added.
+
Setup webserver configuration. See :func:`webserver`.
:param table options: A table with key: value pairs with webserver options.
* ``apiKey=newKey``: string - Changes the API Key (set to an empty string do disable it). Since 1.7.0 the key should be hashed and salted via the :func:`hashPassword` command.
* ``customHeaders={[str]=str,...}``: map of string - Allows setting custom headers and removing the defaults.
* ``acl=newACL``: string - List of IP addresses, as a string, that are allowed to open a connection to the web server. Defaults to "127.0.0.1, ::1".
+ * ``apiRequiresAuthentication``: bool - Whether access to the API (/api endpoints) require a valid API key. Defaults to true.
* ``statsRequireAuthentication``: bool - Whether access to the statistics (/metrics and /jsonstat endpoints) require a valid password or API key. Defaults to true.
* ``maxConcurrentConnections``: int - The maximum number of concurrent web connections, or 0 which means an unlimited number. Defaults to 100.
* ``hashPlaintextCredentials``: bool - Whether passwords and API keys provided in plaintext should be hashed during startup, to prevent the plaintext versions from staying in memory. Doing so increases significantly the cost of verifying credentials. Defaults to false.
self.assertTrue(r)
self.assertEqual(r.status_code, 200)
+class TestAPIWithoutAuthentication(APITestsBase):
+ __test__ = True
+ _apiPath = '/api/v1/servers/localhost/config'
+ # paths accessible using basic auth only (list not exhaustive)
+ _basicOnlyPath = '/'
+ _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed']
+ _config_template = """
+ setACL({"127.0.0.1/32", "::1/128"})
+ newServer({address="127.0.0.1:%s"})
+ webserver("127.0.0.1:%s")
+ setWebserverConfig({password="%s", apiRequiresAuthentication=false })
+ """
+
+ def testAuth(self):
+ """
+ API: API do not require authentication
+ """
+
+ for path in [self._apiPath]:
+ url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+
+ r = requests.get(url, timeout=self._webTimeout)
+ self.assertTrue(r)
+ self.assertEqual(r.status_code, 200)
+
+ # these should still require basic authentication
+ for path in [self._basicOnlyPath]:
+ url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+
+ r = requests.get(url, timeout=self._webTimeout)
+ self.assertEqual(r.status_code, 401)
+
+ r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+ self.assertTrue(r)
+ self.assertEqual(r.status_code, 200)
+
class TestCustomLuaEndpoint(APITestsBase):
__test__ = True
_config_template = """