summaryrefslogtreecommitdiff
path: root/src/buildtool/auth/authentication.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildtool/auth/authentication.hpp')
-rw-r--r--src/buildtool/auth/authentication.hpp269
1 files changed, 162 insertions, 107 deletions
diff --git a/src/buildtool/auth/authentication.hpp b/src/buildtool/auth/authentication.hpp
index ae45561a..cd4ff86d 100644
--- a/src/buildtool/auth/authentication.hpp
+++ b/src/buildtool/auth/authentication.hpp
@@ -14,6 +14,7 @@
#ifndef INCLUDED_SRC_BUILDTOOL_AUTH_AUTHENTICATION_HPP
#define INCLUDED_SRC_BUILDTOOL_AUTH_AUTHENTICATION_HPP
+
#include <cstdint>
#include <filesystem>
#include <fstream>
@@ -25,135 +26,189 @@
#include "gsl/gsl"
#include "src/buildtool/logging/log_level.hpp"
#include "src/buildtool/logging/logger.hpp"
-enum class AuthMethod : std::uint8_t { kNONE, kTLS };
+#include "src/utils/cpp/expected.hpp"
+
+struct Auth final {
+ struct TLS final {
+ class Builder;
+
+ // CA certificate bundle
+ std::string const ca_cert = {};
+ // Client-side signed certificate
+ std::string const client_cert = {};
+ // Client-side private key
+ std::string const client_key = {};
+ // Server-side signed certificate
+ std::string const server_cert = {};
+ // Server-side private key
+ std::string const server_key = {};
+ };
-class Auth {
+ std::variant<std::monostate, TLS> method = {};
+};
+
+class Auth::TLS::Builder final {
public:
- [[nodiscard]] static auto Instance() noexcept -> Auth& {
- static Auth instance{};
- return instance;
+ auto SetCACertificate(
+ std::optional<std::filesystem::path> cert_file) noexcept -> Builder& {
+ ca_cert_file_ = std::move(cert_file);
+ return *this;
}
- void SetAuthMethod(AuthMethod x) { auth_ = x; }
- [[nodiscard]] auto GetAuthMethod() const noexcept -> AuthMethod {
- return auth_;
+ auto SetClientCertificate(
+ std::optional<std::filesystem::path> cert_file) noexcept -> Builder& {
+ client_cert_file_ = std::move(cert_file);
+ return *this;
}
- class TLS {
- public:
- [[nodiscard]] static auto Instance() noexcept -> TLS& {
- static TLS instance{};
- return instance;
- }
-
- [[nodiscard]] auto CACert() const noexcept -> const std::string& {
- return ca_cert_;
- }
-
- [[nodiscard]] auto ClientCert() const noexcept -> const std::string& {
- return client_cert_;
- }
-
- [[nodiscard]] auto ClientKey() const noexcept -> const std::string& {
- return client_key_;
- }
-
- [[nodiscard]] auto ServerCert() const noexcept -> const std::string& {
- return server_cert_;
- }
+ auto SetClientKey(std::optional<std::filesystem::path> key_file) noexcept
+ -> Builder& {
+ client_key_file_ = std::move(key_file);
+ return *this;
+ }
- [[nodiscard]] auto ServerKey() const noexcept -> const std::string& {
- return server_key_;
- }
+ auto SetServerCertificate(
+ std::optional<std::filesystem::path> cert_file) noexcept -> Builder& {
+ server_cert_file_ = std::move(cert_file);
+ return *this;
+ }
- [[nodiscard]] auto SetCACertificate(
- std::filesystem::path const& cert_file) noexcept -> bool {
- return set(cert_file, &ca_cert_);
- }
+ auto SetServerKey(std::optional<std::filesystem::path> key_file) noexcept
+ -> Builder& {
+ server_key_file_ = std::move(key_file);
+ return *this;
+ }
- [[nodiscard]] auto SetClientCertificate(
- std::filesystem::path const& cert_file) noexcept -> bool {
- return set(cert_file, &client_cert_);
+ /// \brief Finalize building, validate the entries, and create an Auth with
+ /// TLS as method. Validation ensures that either both tls_client_cert or
+ /// tls_client_key are set, or none of the two.
+ /// \return Auth on success, error string on failure, nullopt if no TLS
+ /// configuration fields were set.
+ [[nodiscard]] auto Build() noexcept
+ -> std::optional<expected<Auth, std::string>> {
+ // To not duplicate default arguments of Auth::TLS in builder,
+ // create a default config and copy default arguments from there.
+ Auth::TLS const default_auth_tls;
+ bool tls_args_exist = false;
+
+ // Set and validate the CA certification.
+ // If provided, the CA certificate bundle should of course not be empty.
+ auto ca_cert = default_auth_tls.ca_cert;
+ if (ca_cert_file_.has_value()) {
+ if (auto content = read(*ca_cert_file_)) {
+ if (content->empty()) {
+ return unexpected(
+ std::string("Please provide tls-ca-cert"));
+ }
+ ca_cert = *std::move(content);
+ tls_args_exist = true;
+ }
+ else {
+ return unexpected(
+ fmt::format("Could not read '{}' CA certificate.",
+ ca_cert_file_->string()));
+ }
}
- [[nodiscard]] auto SetClientKey(
- std::filesystem::path const& key_file) noexcept -> bool {
- return set(key_file, &client_key_);
+ // Set and validate the client-side certification.
+ // To enable mTLS, both tls_client_{certificate,key} must be supplied.
+ auto client_cert = default_auth_tls.client_cert;
+ if (client_cert_file_.has_value()) {
+ if (auto content = read(*client_cert_file_)) {
+ client_cert = *std::move(content);
+ tls_args_exist = true;
+ }
+ else {
+ return unexpected(
+ fmt::format("Could not read '{}' client certificate.",
+ client_cert_file_->string()));
+ }
}
-
- [[nodiscard]] auto SetServerCertificate(
- std::filesystem::path const& cert_file) noexcept -> bool {
- return set(cert_file, &server_cert_);
+ auto client_key = default_auth_tls.client_key;
+ if (client_key_file_.has_value()) {
+ if (auto content = read(*client_key_file_)) {
+ client_key = *std::move(content);
+ tls_args_exist = true;
+ }
+ else {
+ return unexpected(fmt::format("Could not read '{}' client key.",
+ client_key_file_->string()));
+ }
}
-
- [[nodiscard]] auto SetServerKey(
- std::filesystem::path const& key_file) noexcept -> bool {
- return set(key_file, &server_key_);
+ if (client_cert.empty() != client_key.empty()) {
+ std::string error = client_cert.empty()
+ ? "Please also provide tls-client-cert"
+ : "Please also provide tls-client-key";
+ return unexpected(std::move(error));
}
- // must be called after the parsing of cmd line arguments
- // we ensure that either both tls_client_cert or tls_client_key are set
- // or none of the two.
- [[nodiscard]] auto Validate() const noexcept -> bool {
- if (CACert().empty()) {
- Logger::Log(LogLevel::Error, "Please provide tls-ca-cert");
- return false;
- }
- // to enable mTLS, both tls_client_{certificate,key} must be
- // supplied
- if (ClientCert().empty() && not(ClientKey().empty())) {
- Logger::Log(LogLevel::Error,
- "Please also provide tls-client-cert");
- return false;
+ // Set and validate the server-side certification.
+ // To enable mTLS, both tls_server_{certificate,key} must be supplied.
+ auto server_cert = default_auth_tls.server_cert;
+ if (server_cert_file_.has_value()) {
+ if (auto content = read(*server_cert_file_)) {
+ server_cert = *std::move(content);
+ tls_args_exist = true;
}
- if (not(ClientCert().empty()) && ClientKey().empty()) {
- Logger::Log(LogLevel::Error,
- "Please also provide tls-client-key");
- return false;
+ else {
+ return unexpected(
+ fmt::format("Could not read '{}' server certificate.",
+ server_cert_file_->string()));
}
-
- // to enable mTLS, both tls_server_{certificate,key} must be
- // supplied
- if (ServerCert().empty() && not(ServerKey().empty())) {
- Logger::Log(LogLevel::Error,
- "Please also provide tls-server-cert");
- return false;
+ }
+ auto server_key = default_auth_tls.server_key;
+ if (server_key_file_.has_value()) {
+ if (auto content = read(*server_key_file_)) {
+ server_key = *std::move(content);
+ tls_args_exist = true;
}
- if (not(ServerCert().empty()) && ServerKey().empty()) {
- Logger::Log(LogLevel::Error,
- "Please also provide tls-server-key");
- return false;
+ else {
+ return unexpected(fmt::format("Could not read '{}' server key.",
+ server_key_file_->string()));
}
- return true;
+ }
+ if (server_cert.empty() != server_key.empty()) {
+ std::string error = server_cert.empty()
+ ? "Please also provide tls-server-cert"
+ : "Please also provide tls-server-key";
+ return unexpected(std::move(error));
}
- private:
- std::string ca_cert_;
- std::string client_cert_;
- std::string client_key_;
- std::string server_cert_;
- std::string server_key_;
- // auxiliary function to set the content of the members of this class
- [[nodiscard]] static auto set(
- std::filesystem::path const& x,
- gsl::not_null<std::string*> const& member) noexcept -> bool {
- Auth::Instance().SetAuthMethod(AuthMethod::kTLS);
- try {
- // if the file does not exist, it will throw an exception
- auto file = std::filesystem::canonical(x);
- std::ifstream cert{file};
- std::string tmp((std::istreambuf_iterator<char>(cert)),
- std::istreambuf_iterator<char>());
- *member = std::move(tmp);
- } catch (std::exception const& e) {
- Logger::Log(LogLevel::Error, e.what());
- return false;
- }
- return true;
+ // If no TLS arguments were ever set, there is nothing to build.
+ if (not tls_args_exist) {
+ return std::nullopt;
}
- };
+
+ // Return an authentication configuration with mTLS enabled.
+ return Auth{.method = Auth::TLS{.ca_cert = std::move(ca_cert),
+ .client_cert = std::move(client_cert),
+ .client_key = std::move(client_key),
+ .server_cert = std::move(server_cert),
+ .server_key = std::move(server_key)}};
+ }
private:
- AuthMethod auth_{AuthMethod::kNONE};
+ std::optional<std::filesystem::path> ca_cert_file_;
+ std::optional<std::filesystem::path> client_cert_file_;
+ std::optional<std::filesystem::path> client_key_file_;
+ std::optional<std::filesystem::path> server_cert_file_;
+ std::optional<std::filesystem::path> server_key_file_;
+
+ /// \brief Auxiliary function to read the content of certification files.
+ [[nodiscard]] static auto read(std::filesystem::path const& x) noexcept
+ -> std::optional<std::string> {
+ try {
+ // if the file does not exist, it will throw an exception
+ auto file = std::filesystem::canonical(x);
+ std::ifstream cert{file};
+ std::string tmp((std::istreambuf_iterator<char>(cert)),
+ std::istreambuf_iterator<char>());
+ return tmp;
+ } catch (std::exception const& e) {
+ Logger::Log(LogLevel::Error, e.what());
+ }
+ return std::nullopt;
+ }
};
-#endif
+
+#endif // INCLUDED_SRC_BUILDTOOL_AUTH_AUTHENTICATION_HPP