summaryrefslogtreecommitdiff
path: root/src/buildtool/auth/authentication.hpp
diff options
context:
space:
mode:
authorPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2024-07-02 16:47:13 +0200
committerPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2024-07-04 16:05:08 +0200
commitc2f7ead468d5e65c57e7ecb49d7fbba4254c46b7 (patch)
treeb96ce2e63d9d6a2d486cb4133c290187156b97ac /src/buildtool/auth/authentication.hpp
parent0d60cd9ba4a5c18b01b6ef996434953071f0576e (diff)
downloadjustbuild-c2f7ead468d5e65c57e7ecb49d7fbba4254c46b7.tar.gz
Replace the Auth and Auth::TLS singletons
Use a builder pattern for creation and validation, in a manner that allows also other authentication methods to be added in the future besides the current TLS/SSL. The main Auth instances are built early and then passed by not_null const pointers, to avoid passing temporaries, replacing the previous Auth::TLS instances passed by simple nullable const pointers. Where needed, these passed Auth instances are also stored, by const ref. Tests also build Auth instances as needed, either with the default 'no certification' or from the test environment arguments.
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