diff --git a/README.md b/README.md
index 287d15e..f3fb00b 100644
--- a/README.md
+++ b/README.md
@@ -123,6 +123,9 @@ const auto response = influxdb->execute("SHOW DATABASES");
An underlying transport is fully configurable by passing an URI:
```
[protocol]://[username:password@]host:port[?db=database]
+
+# Auth token:
+[protocol]://[token@]host:port[?db=database]
```
List of supported transport is following:
diff --git a/src/HTTP.cxx b/src/HTTP.cxx
index c2b26ef..4ac9de4 100644
--- a/src/HTTP.cxx
+++ b/src/HTTP.cxx
@@ -96,10 +96,15 @@ namespace influxdb::transports
session.SetAuth(cpr::Authentication{user, pass, cpr::AuthMode::BASIC});
}
+ void HTTP::setAuthToken(const std::string& token)
+ {
+ session.UpdateHeader(cpr::Header{{"Authorization", "Token " + token}});
+ }
+
void HTTP::send(std::string&& lineprotocol)
{
session.SetUrl(cpr::Url{endpointUrl + "/write"});
- session.SetHeader(cpr::Header{{"Content-Type", "application/json"}});
+ session.UpdateHeader(cpr::Header{{"Content-Type", "application/json"}});
session.SetParameters(cpr::Parameters{{"db", databaseName}});
session.SetBody(cpr::Body{lineprotocol});
diff --git a/src/HTTP.h b/src/HTTP.h
index 8b2c029..66ccf02 100644
--- a/src/HTTP.h
+++ b/src/HTTP.h
@@ -64,6 +64,10 @@ namespace influxdb::transports
/// \param pass password
void setBasicAuthentication(const std::string& user, const std::string& pass);
+ /// Sets the API token for authentication
+ /// \param token API token
+ void setAuthToken(const std::string& token);
+
/// Sets proxy
void setProxy(const Proxy& proxy) override;
diff --git a/src/InfluxDBFactory.cxx b/src/InfluxDBFactory.cxx
index 2c64d14..4832690 100644
--- a/src/InfluxDBFactory.cxx
+++ b/src/InfluxDBFactory.cxx
@@ -42,10 +42,14 @@ namespace influxdb
std::unique_ptr withHttpTransport(const http::url& uri)
{
auto transport = std::make_unique(uri.url);
- if (!uri.user.empty())
+ if (!uri.user.empty() && !uri.password.empty())
{
transport->setBasicAuthentication(uri.user, uri.password);
}
+ else if (!uri.password.empty())
+ {
+ transport->setAuthToken(uri.password);
+ }
return transport;
}
diff --git a/src/UriParser.h b/src/UriParser.h
index 0aebff9..8e6877b 100644
--- a/src/UriParser.h
+++ b/src/UriParser.h
@@ -115,8 +115,15 @@ namespace http
const auto search = ExtractSearch(in);
const auto path = ExtractPath(in);
std::string userpass = ExtractUserpass(in);
- const auto password = ExtractPassword(userpass);
- const auto user = userpass;
+
+ auto [password, user] = [](auto str)
+ {
+ if (str.find(":") != std::string::npos)
+ {
+ return std::make_pair(ExtractPassword(str), str);
+ }
+ return std::make_pair(str, std::string{""});
+ }(userpass);
const auto port = ExtractPort(in);
const auto host = in;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 7ac6bc3..333ab1b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -37,6 +37,7 @@ add_unittest(InfluxDBTest DEPENDS InfluxDB)
add_unittest(InfluxDBFactoryTest DEPENDS InfluxDB)
add_unittest(ProxyTest DEPENDS InfluxDB)
add_unittest(HttpTest DEPENDS InfluxDB-Core InfluxDB-Internal InfluxDB-BoostSupport CprMock Threads::Threads)
+add_unittest(UriParserTest)
add_unittest(NoBoostSupportTest)
target_sources(NoBoostSupportTest PRIVATE ${PROJECT_SOURCE_DIR}/src/NoBoostSupport.cxx)
@@ -53,6 +54,7 @@ add_custom_target(unittest PointTest
COMMAND InfluxDBFactoryTest
COMMAND ProxyTest
COMMAND HttpTest
+ COMMAND UriParserTest
COMMAND NoBoostSupportTest
COMMAND $<$,$>>:BoostSupportTest>
diff --git a/test/HttpTest.cxx b/test/HttpTest.cxx
index 416159b..3344105 100644
--- a/test/HttpTest.cxx
+++ b/test/HttpTest.cxx
@@ -82,7 +82,7 @@ namespace influxdb::test
REQUIRE_CALL(sessionMock, Post()).RETURN(createResponse(cpr::ErrorCode::OK, cpr::status::HTTP_OK));
REQUIRE_CALL(sessionMock, SetUrl(eq("http://localhost:8086/write")));
- REQUIRE_CALL(sessionMock, SetHeader(_)).WITH(_1.at("Content-Type") == "application/json");
+ REQUIRE_CALL(sessionMock, UpdateHeader(_)).WITH(_1.at("Content-Type") == "application/json");
REQUIRE_CALL(sessionMock, SetBody(_)).WITH(_1.str() == data);
REQUIRE_CALL(sessionMock, SetParameters(ParamMap{{"db", "test"}}));
@@ -95,7 +95,7 @@ namespace influxdb::test
REQUIRE_CALL(sessionMock, Post()).RETURN(createResponse(cpr::ErrorCode::INTERNAL_ERROR, cpr::status::HTTP_OK));
ALLOW_CALL(sessionMock, SetUrl(_));
- ALLOW_CALL(sessionMock, SetHeader(_));
+ ALLOW_CALL(sessionMock, UpdateHeader(_));
ALLOW_CALL(sessionMock, SetBody(_));
ALLOW_CALL(sessionMock, SetParameters(_));
@@ -108,7 +108,7 @@ namespace influxdb::test
REQUIRE_CALL(sessionMock, Post()).RETURN(createResponse(cpr::ErrorCode::OK, cpr::status::HTTP_OK));
ALLOW_CALL(sessionMock, SetUrl(_));
- ALLOW_CALL(sessionMock, SetHeader(_));
+ ALLOW_CALL(sessionMock, UpdateHeader(_));
ALLOW_CALL(sessionMock, SetBody(_));
ALLOW_CALL(sessionMock, SetParameters(_));
@@ -121,7 +121,7 @@ namespace influxdb::test
REQUIRE_CALL(sessionMock, Post()).RETURN(createResponse(cpr::ErrorCode::OK, cpr::status::HTTP_NOT_FOUND));
ALLOW_CALL(sessionMock, SetUrl(_));
- ALLOW_CALL(sessionMock, SetHeader(_));
+ ALLOW_CALL(sessionMock, UpdateHeader(_));
ALLOW_CALL(sessionMock, SetBody(_));
ALLOW_CALL(sessionMock, SetParameters(_));
@@ -225,6 +225,14 @@ namespace influxdb::test
http.setBasicAuthentication("user0", "pass0");
}
+ TEST_CASE("Set auth token sets auth header", "[HttpTest]")
+ {
+ auto http = createHttp();
+
+ REQUIRE_CALL(sessionMock, UpdateHeader(_)).WITH(_1.at("Authorization") == "Token not-a-real-api-token");
+ http.setAuthToken("not-a-real-api-token");
+ }
+
TEST_CASE("Set proxy without authentication", "[HttpTest]")
{
auto http = createHttp();
diff --git a/test/UriParserTest.cxx b/test/UriParserTest.cxx
new file mode 100644
index 0000000..f2335cd
--- /dev/null
+++ b/test/UriParserTest.cxx
@@ -0,0 +1,83 @@
+// MIT License
+//
+// Copyright (c) 2020-2023 offa
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#include "UriParser.h"
+#include
+#include
+
+namespace influxdb::test
+{
+ namespace
+ {
+ http::url parse(std::string in)
+ {
+ return http::ParseHttpUrl(in);
+ }
+ }
+
+
+ TEST_CASE("Parse url", "[UriParserTest]")
+ {
+ const std::string input{"https://xyz.com/fghi/jklm"};
+ const auto url = parse(input);
+
+ CHECK(url.protocol == "https");
+ CHECK(url.user == "");
+ CHECK(url.password == "");
+ CHECK(url.host == "xyz.com");
+ CHECK(url.path == "/fghi/jklm");
+ CHECK(url.search == "");
+ CHECK(url.url == input);
+ CHECK(url.port == 0);
+ }
+
+ TEST_CASE("Parse protocol", "[UriParserTest]")
+ {
+ CHECK(parse("http://xyz.com").protocol == "http");
+ CHECK(parse("https://xyz.com").protocol == "https");
+ CHECK(parse("udp://xyz.com").protocol == "udp");
+ CHECK(parse("unix://xyz.com").protocol == "unix");
+ }
+
+ TEST_CASE("Parse param", "[UriParserTest]")
+ {
+ CHECK(parse("http://xyz.com/aaa?param=value").search == "param=value");
+ }
+
+ TEST_CASE("Parse port", "[UriParserTest]")
+ {
+ CHECK(parse("http://xyz.com:12345").port == 12345);
+ }
+
+ TEST_CASE("Parse basic auth", "[UriParserTest]")
+ {
+ const auto url = parse("https://aaa:bbb@host0");
+ CHECK(url.user == "aaa");
+ CHECK(url.password == "bbb");
+ }
+
+ TEST_CASE("Parse auth token", "[UriParserTest]")
+ {
+ CHECK(parse("http://token@xyz.com").password == "token");
+ }
+
+}
diff --git a/test/mock/CprMock.cxx b/test/mock/CprMock.cxx
index b820aad..9a53bac 100644
--- a/test/mock/CprMock.cxx
+++ b/test/mock/CprMock.cxx
@@ -105,6 +105,10 @@ namespace cpr
influxdb::test::sessionMock.SetHeader(header);
}
+ void Session::UpdateHeader(const Header& header)
+ {
+ influxdb::test::sessionMock.UpdateHeader(header);
+ }
Parameters::Parameters(const std::initializer_list& parameters)
: CurlContainer(parameters)
diff --git a/test/mock/CprMock.h b/test/mock/CprMock.h
index 400a9b7..210572d 100644
--- a/test/mock/CprMock.h
+++ b/test/mock/CprMock.h
@@ -39,6 +39,7 @@ namespace influxdb::test
MAKE_MOCK0(Post, cpr::Response());
MAKE_MOCK1(SetUrl, void(const cpr::Url&));
MAKE_MOCK1(SetHeader, void(const cpr::Header&));
+ MAKE_MOCK1(UpdateHeader, void(const cpr::Header&));
MAKE_MOCK1(SetBody, void(cpr::Body&&));
MAKE_MOCK1(SetParameters, void(std::map));
MAKE_MOCK1(SetAuth, void(const cpr::Authentication&));