Skip to content

Commit

Permalink
Use JSON to store Credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
marcosbento committed Jun 5, 2024
1 parent 4ecfbee commit 09a069c
Show file tree
Hide file tree
Showing 12 changed files with 434 additions and 48 deletions.
6 changes: 4 additions & 2 deletions libs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ set(srcs
$<$<BOOL:${OPENSSL_FOUND}>:server/src/ecflow/server/SslTcpServer.cpp>

# Service -- Headers
service/src/ecflow/service/auth/Credentials.hpp
service/src/ecflow/service/aviso/Aviso.hpp
service/src/ecflow/service/aviso/AvisoService.hpp
service/src/ecflow/service/aviso/etcd/Client.hpp
Expand All @@ -512,6 +513,7 @@ set(srcs
service/src/ecflow/service/Log.hpp
service/src/ecflow/service/Registry.hpp
# Service -- Sources
service/src/ecflow/service/auth/Credentials.cpp
service/src/ecflow/service/aviso/Aviso.cpp
service/src/ecflow/service/aviso/AvisoService.cpp
service/src/ecflow/service/aviso/etcd/Range.cpp
Expand Down Expand Up @@ -540,11 +542,11 @@ set(srcs

ecbuild_add_library(
TARGET
ecflow_all
ecflow_all
NOINSTALL
TYPE STATIC
SOURCES
${srcs}
${srcs}
PUBLIC_INCLUDES
attribute/src
base/src
Expand Down
35 changes: 32 additions & 3 deletions libs/service/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# ecflow_services
# --------------------------------------------------

# u_services
# u_service_executor
# --------------------------------------------------

ecbuild_add_test(
Expand All @@ -31,6 +31,33 @@ target_clangformat(u_service_executor
CONDITION ENABLE_TESTS
)

# u_service_auth
# --------------------------------------------------

ecbuild_add_test(
TARGET
u_service_auth
LABELS
unit nightly
SOURCES
# Headers -- utilities
test/TestContentProvider.hpp
# Sources -- utilities
test/TestContentProvider.cpp
# Sources
test/auth/TestAuth_main.cpp # test entry point
test/auth/TestAuth.cpp
INCLUDES
test
LIBS
ecflow_all
Boost::boost # Boost header-only libraries must be available (namely unit_test_framework)
Boost::filesystem
)
target_clangformat(u_service_auth
CONDITION ENABLE_TESTS
)

# u_service_aviso
# --------------------------------------------------

Expand All @@ -41,13 +68,15 @@ ecbuild_add_test(
unit nightly
SOURCES
# Headers -- utilities
test/aviso/TestContentProvider.hpp
test/TestContentProvider.hpp
# Sources -- utilities
test/aviso/TestContentProvider.cpp
test/TestContentProvider.cpp
# Sources
test/aviso/TestAviso_main.cpp # test entry point
test/aviso/TestAviso.cpp
test/aviso/TestAvisoService.cpp
INCLUDES
test
LIBS
ecflow_all
Boost::boost # Boost header-only libraries must be available (namely unit_test_framework)
Expand Down
91 changes: 91 additions & 0 deletions libs/service/src/ecflow/service/auth/Credentials.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright 2009- ECMWF.
*
* This software is licensed under the terms of the Apache Licence version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation
* nor does it submit to any jurisdiction.
*/

#include "ecflow/service/auth/Credentials.hpp"

#include <fstream>

#include <nlohmann/json.hpp>

#include "ecflow/core/Message.hpp"

namespace ecf::service::auth {

void Credentials::add(std::string key, std::string value) {
entries_.push_back({std::move(key), std::move(value)});
}

std::optional<std::string> Credentials::value(std::string_view key) const {
for (const auto& entry : entries_) {
if (entry.key == key) {
return entry.value;
}
}
return std::nullopt;
}

std::optional<Credentials::UserCredentials> Credentials::user() const {
if (auto username = value("username"); username) {
if (auto password = value("password"); password) {
return UserCredentials{std::move(*username), std::move(*password)};
}
}
return std::nullopt;
}

std::optional<Credentials::KeyCredentials> Credentials::key() const {
if (auto key = value("key"); key) {
return KeyCredentials{std::move(*key)};
}
return std::nullopt;
}

namespace {

Credentials load_from_stream(std::istream& input) {
using json = nlohmann::ordered_json;

json content;
try {
content = json::parse(input);
}
catch (const json::parse_error& e) {
throw std::runtime_error(Message("Credentials: Unable to parse content, due to ", e.what()).str());
}

Credentials credentials;
for (auto field: content.items()) {
try {
credentials.add(field.key(), field.value());
} catch (const json::type_error& e) {
throw std::runtime_error(Message("Credentials: Unable to retrieve content, due to ", e.what()).str());
}
}

if (!credentials.user() && !credentials.key()) {
throw std::runtime_error("Credentials: Invalid content found (neither user nor key credentials provided)");
}

return credentials;
}

} // namespace

Credentials Credentials::load(const std::string& filepath) {
std::ifstream stream(filepath);
return load_from_stream(stream);
}

Credentials Credentials::load_content(const std::string& content) {
std::istringstream stream(content);
return load_from_stream(stream);
}

} // namespace ecf::service::auth
57 changes: 57 additions & 0 deletions libs/service/src/ecflow/service/auth/Credentials.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2009- ECMWF.
*
* This software is licensed under the terms of the Apache Licence version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation
* nor does it submit to any jurisdiction.
*/

#ifndef ecflow_service_auth_Credentials_HPP
#define ecflow_service_auth_Credentials_HPP

#include <optional>
#include <string>
#include <vector>

namespace ecf::service::auth {

class Credentials {
public:
struct UserCredentials
{
std::string username;
std::string password;
};

struct KeyCredentials
{
std::string key;
};

Credentials() = default;

void add(std::string key, std::string value);

[[nodiscard]] std::optional<std::string> value(std::string_view key) const;

[[nodiscard]] std::optional<UserCredentials> user() const;
[[nodiscard]] std::optional<KeyCredentials> key() const;

static Credentials load(const std::string& filepath);
static Credentials load_content(const std::string& content);

private:
struct Entry
{
std::string key;
std::string value;
};

std::vector<Entry> entries_;
};

} // namespace ecf::service::auth

#endif /* ecflow_service_auth_Credentials_HPP */
26 changes: 8 additions & 18 deletions libs/service/src/ecflow/service/aviso/AvisoService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,11 @@

#include "ecflow/core/Overload.hpp"
#include "ecflow/service/Registry.hpp"
#include "ecflow/service/auth/Credentials.hpp"
#include "ecflow/service/aviso/etcd/Client.hpp"

namespace ecf::service::aviso {

namespace {

std::string load_authentication_credential(const std::string& auth_file) {
std::ifstream file(auth_file);
if (!file.is_open()) {
throw std::runtime_error("Unable to open file: " + auth_file);
}

std::string token;
std::getline(file, token);
file.close();

return token;
}

} // namespace

std::ostream& operator<<(std::ostream& os, const AvisoResponse& r) {
std::visit(ecf::overload{[&os](const NotificationPackage<ConfiguredListener, AvisoNotification>& p) { os << p; },
[&os](const AvisoNoMatch& a) { os << a; },
Expand Down Expand Up @@ -144,7 +128,13 @@ void AvisoService::register_listener(const AvisoSubscribe& listen) {
auto& inserted = listeners_.emplace_back(listener);

if (auto auth = listen.auth(); !auth.empty()) {
inserted.auth_token = load_authentication_credential(listen.auth());
auto credentials = ecf::service::auth::Credentials::load(auth);
if (auto key_credentials = credentials.key(); key_credentials) {
inserted.auth_token = key_credentials->key;
}
else {
SLOG(I, "AvisoService: no key found in auth token for listener {" << listener.path() << "}");
}
}
}

Expand Down
1 change: 0 additions & 1 deletion libs/service/src/ecflow/service/aviso/etcd/Client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ namespace ecf::service::aviso::etcd {

class Client {
public:
Client() = default;
Client(const std::string& address);
Client(const std::string& address, const std::string& auth_token);

Expand Down
30 changes: 8 additions & 22 deletions libs/service/src/ecflow/service/mirror/MirrorService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,10 @@
#include "ecflow/node/Node.hpp"
#include "ecflow/service/Log.hpp"
#include "ecflow/service/Registry.hpp"
#include "ecflow/service/auth/Credentials.hpp"

namespace ecf::service::mirror {

namespace {

std::pair<std::string, std::string> load_auth_credentials(const std::string& auth_file) {
std::ifstream file(auth_file);
if (!file.is_open()) {
throw std::runtime_error("Unable to open file: " + auth_file);
}

std::string username, password;
std::getline(file, username);
std::getline(file, password);
file.close();

return {username, password};
}

} // namespace

/* MirrorService */

void MirrorService::start() {
Expand Down Expand Up @@ -115,9 +98,11 @@ void MirrorService::register_listener(const MirrorRequest& request) {
if (!request.auth.empty()) {
SLOG(D, "MirrorService: Loading auth {" << request.auth << "}");
try {
auto [username, password] = load_auth_credentials(request.auth);
inserted.remote_username_ = username;
inserted.remote_password_ = password;
auto credentials = ecf::service::auth::Credentials::load(request.auth);
if (auto user = credentials.user(); user) {
inserted.remote_username_ = user->username;
inserted.remote_password_ = user->password;
}
}
catch (std::runtime_error& e) {
throw std::runtime_error("MirrorService: Unable to load auth credentials");
Expand All @@ -132,7 +117,8 @@ MirrorController::MirrorController()
if (auto* server = TheOneServer::server(); server) {
// The following forces the server to increment the job generation count and traverse the defs
server->increment_job_generation_count();
} else {
}
else {
SLOG(E, "MirrorController: no server available, thus unable to increment job generation count");
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ std::string make_temp_filename(std::string file_name_prefix) {
std::array<char, 4096> file_name_template{};
std::copy(file_name_prefix.begin(), file_name_prefix.end(), file_name_template.data());
mkstemp(file_name_template.data());
return file_name_prefix;
return file_name_template.data();
}

void store_content_to_file(const std::string& file_path, const std::string& content) {
Expand Down
File renamed without changes.
Loading

0 comments on commit 09a069c

Please sign in to comment.