Allow passing secrets to build action
This commit is contained in:
parent
df2b5ba9f6
commit
255da5b091
|
@ -85,7 +85,7 @@ use_cpp_utilities(VISIBILITY PUBLIC)
|
|||
|
||||
# find passwordfile
|
||||
find_package(passwordfile${CONFIGURATION_PACKAGE_SUFFIX} 5.0.0 REQUIRED)
|
||||
use_password_file(VISIBILITY PUBLIC)
|
||||
use_password_file()
|
||||
|
||||
# find boost libraries
|
||||
option(BOOST_STATIC_LINKAGE "${STATIC_LINKAGE}" "link statically against Boost (instead of dynamically)")
|
||||
|
|
|
@ -13,6 +13,7 @@ enum class UserPermissions : std::uint64_t {
|
|||
ModifyBuildActions = ReadBuildActionsDetails | DownloadArtefacts | (1 << 2),
|
||||
PerformAdminActions = (1 << 3),
|
||||
TryAgain = (1 << 4),
|
||||
AccessSecrets = (1 << 5),
|
||||
DefaultPermissions = ReadBuildActionsDetails,
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "../webapi/session.h"
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
|
||||
#include <reflective_rapidjson/binary/reflector-chronoutilities.h>
|
||||
#include <reflective_rapidjson/json/reflector-chronoutilities.h>
|
||||
|
||||
|
@ -259,7 +261,7 @@ bool BuildAction::haveSucceeded(const std::vector<std::shared_ptr<BuildAction>>
|
|||
* the build action is setup-globally visible.
|
||||
* \returns Returns immediately. The real work is done in a build action thread.
|
||||
*/
|
||||
LibPkg::StorageID BuildAction::start(ServiceSetup &setup)
|
||||
LibPkg::StorageID BuildAction::start(ServiceSetup &setup, std::unique_ptr<Io::PasswordFile> &&secrets)
|
||||
{
|
||||
if (!isScheduled()) {
|
||||
return 0;
|
||||
|
@ -269,6 +271,13 @@ LibPkg::StorageID BuildAction::start(ServiceSetup &setup)
|
|||
status = BuildActionStatus::Running;
|
||||
m_setup = &setup;
|
||||
|
||||
// grab secrets from session
|
||||
// note: That's done regardless of the type because we might need to pass the secrets to the next
|
||||
// action in the chain (regardless of the current build action's type).
|
||||
if (secrets) {
|
||||
m_secrets = std::move(secrets);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case BuildActionType::Invalid:
|
||||
resultData = "type is invalid";
|
||||
|
@ -366,7 +375,13 @@ LibPkg::StorageID BuildAction::conclude(BuildActionResult result)
|
|||
const auto followUps = m_setup->building.followUpBuildActions(id);
|
||||
for (auto &followUpAction : followUps) {
|
||||
if (followUpAction->isScheduled() && BuildAction::haveSucceeded(m_setup->building.getBuildActions(followUpAction->startAfter))) {
|
||||
followUpAction->start(*m_setup);
|
||||
auto secrets = std::unique_ptr<Io::PasswordFile>();
|
||||
if (m_secrets) {
|
||||
secrets = std::make_unique<Io::PasswordFile>();
|
||||
secrets->setPath(m_secrets->path());
|
||||
secrets->setPassword(m_secrets->password());
|
||||
}
|
||||
followUpAction->start(*m_setup, std::move(secrets));
|
||||
}
|
||||
}
|
||||
// note: Not cleaning up the follow-up actions here because at some point I might implement recursive restarting.
|
||||
|
|
|
@ -36,6 +36,10 @@
|
|||
|
||||
class BuildActionsTests;
|
||||
|
||||
namespace Io {
|
||||
class PasswordFile;
|
||||
}
|
||||
|
||||
namespace LibRepoMgr {
|
||||
|
||||
struct ServiceSetup;
|
||||
|
@ -208,7 +212,7 @@ public:
|
|||
static bool haveSucceeded(const std::vector<std::shared_ptr<BuildAction>> &buildActions);
|
||||
bool isAborted() const;
|
||||
const std::atomic_bool &aborted() const;
|
||||
LibPkg::StorageID start(ServiceSetup &setup);
|
||||
LibPkg::StorageID start(ServiceSetup &setup, std::unique_ptr<Io::PasswordFile> &&secrets);
|
||||
void assignStartAfter(const std::vector<std::shared_ptr<BuildAction>> &startsAfterBuildActions);
|
||||
void abort();
|
||||
void appendOutput(std::string_view output);
|
||||
|
@ -256,6 +260,7 @@ private:
|
|||
std::mutex m_outputSessionMutex;
|
||||
std::shared_ptr<BuildProcessSession> m_outputSession;
|
||||
std::unique_ptr<InternalBuildAction> m_internalBuildAction;
|
||||
std::unique_ptr<Io::PasswordFile> m_secrets;
|
||||
};
|
||||
|
||||
inline bool BuildActionBase::isScheduled() const
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <reflective_rapidjson/binary/serializable.h>
|
||||
#include <reflective_rapidjson/json/errorformatting.h>
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
#include <c++utilities/conversion/stringbuilder.h>
|
||||
#include <c++utilities/conversion/stringconversion.h>
|
||||
|
@ -307,7 +309,8 @@ StorageID ServiceSetup::BuildSetup::storeBuildAction(const std::shared_ptr<Build
|
|||
}
|
||||
// immediately start if all follow-up actions have succeeded
|
||||
if (allSucceeded && buildAction->setup()) {
|
||||
return buildAction->start(*buildAction->setup());
|
||||
// FIXME: Do we actually ever get here?
|
||||
return buildAction->start(*buildAction->setup(), std::unique_ptr<Io::PasswordFile>());
|
||||
}
|
||||
} else {
|
||||
for (const auto id : buildAction->startAfter) {
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "../buildactions/buildactionprivate.h"
|
||||
#include "../buildactions/subprocess.h"
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
|
||||
#include <c++utilities/conversion/stringconversion.h>
|
||||
#include <c++utilities/io/ansiescapecodes.h>
|
||||
#include <c++utilities/io/misc.h>
|
||||
|
@ -72,6 +74,7 @@ private:
|
|||
|
||||
std::string m_configDbFile, m_buildingDbFile;
|
||||
ServiceSetup m_setup;
|
||||
std::unique_ptr<Io::PasswordFile> m_secrets;
|
||||
std::shared_ptr<BuildAction> m_buildAction;
|
||||
std::filesystem::path m_workingDir;
|
||||
double m_timeoutFactor = 0.0;
|
||||
|
@ -190,7 +193,7 @@ void BuildActionsTests::resetBuildAction()
|
|||
void BuildActionsTests::runBuildAction(const char *message, CppUtilities::TimeSpan timeout)
|
||||
{
|
||||
resetBuildAction();
|
||||
m_buildAction->start(m_setup);
|
||||
m_buildAction->start(m_setup, std::move(m_secrets));
|
||||
auto &ioc = m_setup.building.ioContext;
|
||||
ioc.restart();
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> workGuard = boost::asio::make_work_guard(ioc);
|
||||
|
|
|
@ -279,7 +279,7 @@ void postBuildAction(const Params ¶ms, ResponseHandler &&handler)
|
|||
// start build action immediately or just add to setup-global list for now
|
||||
auto buildLock2 = params.setup.building.lockToWrite();
|
||||
if (startImmediately) {
|
||||
buildAction->start(params.setup);
|
||||
buildAction->start(params.setup, params.session.secrets());
|
||||
} else {
|
||||
params.setup.building.storeBuildAction(buildAction);
|
||||
}
|
||||
|
@ -385,7 +385,7 @@ static std::vector<std::shared_ptr<BuildAction>> allocateBuildActionIDs(ServiceS
|
|||
return previousActions;
|
||||
}
|
||||
|
||||
static bool startFirstBuildActions(ServiceSetup &setup, SequencedBuildActions &newActionSequence)
|
||||
static bool startFirstBuildActions(const Params ¶ms, SequencedBuildActions &newActionSequence)
|
||||
{
|
||||
auto handledFirstAction = false;
|
||||
for (auto &sequencedAction : newActionSequence.actions) {
|
||||
|
@ -393,13 +393,13 @@ static bool startFirstBuildActions(ServiceSetup &setup, SequencedBuildActions &n
|
|||
auto &action = *maybeAction;
|
||||
handledFirstAction = true;
|
||||
if (action->isScheduled()) {
|
||||
action->start(setup);
|
||||
action->start(params.setup, params.session.secrets());
|
||||
}
|
||||
if (!newActionSequence.concurrent) {
|
||||
return true;
|
||||
}
|
||||
} else if (auto *const subSequence = std::get_if<SequencedBuildActions>(&sequencedAction)) {
|
||||
if (startFirstBuildActions(setup, *subSequence) && !newActionSequence.concurrent) {
|
||||
if (startFirstBuildActions(params, *subSequence) && !newActionSequence.concurrent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -475,7 +475,7 @@ void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, c
|
|||
|
||||
// start first build action immediately (read-lock sufficient because build action not part of setup-global list yet)
|
||||
if (startNow) {
|
||||
startFirstBuildActions(params.setup, newActionSequence);
|
||||
startFirstBuildActions(params, newActionSequence);
|
||||
}
|
||||
|
||||
// add build actions to setup-global list
|
||||
|
@ -547,7 +547,7 @@ void postCloneBuildActions(const Params ¶ms, ResponseHandler &&handler)
|
|||
clone->type = orig->type;
|
||||
clone->startAfter = orig->startAfter;
|
||||
if (startImmediately) {
|
||||
clone->start(params.setup);
|
||||
clone->start(params.setup, params.session.secrets());
|
||||
}
|
||||
params.setup.building.storeBuildAction(std::move(clone));
|
||||
cloneIds.emplace_back(id);
|
||||
|
@ -572,7 +572,7 @@ void postStartBuildActions(const Params ¶ms, ResponseHandler &&handler)
|
|||
return;
|
||||
}
|
||||
for (auto &action : buildActionsSearchResult.actions) {
|
||||
action->start(params.setup);
|
||||
action->start(params.setup, params.session.secrets());
|
||||
}
|
||||
buildActionsSearchResult.lock = std::monostate{};
|
||||
handler(makeText(params.request(), "ok"));
|
||||
|
|
|
@ -35,9 +35,9 @@ const Router Server::s_router = {
|
|||
{ { http::verb::get, "/api/v0/build-action/details" }, Route{&Routes::getBuildActionDetails, UserPermissions::ReadBuildActionsDetails} },
|
||||
{ { http::verb::get, "/api/v0/build-action/logfile" }, Route{&Routes::getBuildActionLogFile, UserPermissions::ReadBuildActionsDetails} },
|
||||
{ { http::verb::get, "/api/v0/build-action/artefact" }, Route{&Routes::getBuildActionArtefact, UserPermissions::DownloadArtefacts} },
|
||||
{ { http::verb::post, "/api/v0/build-action" }, Route{&Routes::postBuildAction, UserPermissions::ModifyBuildActions} },
|
||||
{ { http::verb::post, "/api/v0/build-action/clone" }, Route{&Routes::postCloneBuildActions, UserPermissions::ModifyBuildActions} },
|
||||
{ { http::verb::post, "/api/v0/build-action/start" }, Route{&Routes::postStartBuildActions, UserPermissions::ModifyBuildActions} },
|
||||
{ { http::verb::post, "/api/v0/build-action" }, Route{&Routes::postBuildAction, UserPermissions::ModifyBuildActions | UserPermissions::AccessSecrets} },
|
||||
{ { http::verb::post, "/api/v0/build-action/clone" }, Route{&Routes::postCloneBuildActions, UserPermissions::ModifyBuildActions | UserPermissions::AccessSecrets} },
|
||||
{ { http::verb::post, "/api/v0/build-action/start" }, Route{&Routes::postStartBuildActions, UserPermissions::ModifyBuildActions | UserPermissions::AccessSecrets} },
|
||||
{ { http::verb::post, "/api/v0/build-action/stop" }, Route{&Routes::postStopBuildActions, UserPermissions::ModifyBuildActions} },
|
||||
{ { http::verb::post, "/api/v0/quit" }, Route{&Routes::postQuit, UserPermissions::PerformAdminActions} },
|
||||
};
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "../serversetup.h"
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
|
||||
#include <c++utilities/conversion/stringbuilder.h>
|
||||
#include <c++utilities/io/ansiescapecodes.h>
|
||||
#include <c++utilities/io/misc.h>
|
||||
|
@ -22,6 +24,17 @@ using namespace CppUtilities::EscapeCodes;
|
|||
namespace LibRepoMgr {
|
||||
namespace WebAPI {
|
||||
|
||||
Session::Session(boost::asio::ip::tcp::socket &&socket, ServiceSetup &setup)
|
||||
: m_socket(std::move(socket))
|
||||
, m_strand(m_socket.get_executor())
|
||||
, m_setup(setup)
|
||||
{
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
}
|
||||
|
||||
void Session::receive()
|
||||
{
|
||||
m_parser = make_unique<RequestParser>();
|
||||
|
@ -89,13 +102,19 @@ void Session::received(boost::system::error_code ec, size_t bytesTransferred)
|
|||
return;
|
||||
}
|
||||
// prepare file with secrets for user
|
||||
if(!userAuth.name.empty() && !userAuth.password.empty()) {
|
||||
if (!userAuth.name.empty() && !userAuth.password.empty()
|
||||
&& (static_cast<PermissionFlags>(requiredPermissions) & static_cast<PermissionFlags>(UserPermissions::AccessSecrets))) {
|
||||
try {
|
||||
m_secrets.clear();
|
||||
m_secrets.setPath(argsToString("secrets/"sv, userAuth.name));
|
||||
m_secrets.setPassword(userAuth.password.data(), userAuth.password.size());
|
||||
if (m_secrets) {
|
||||
m_secrets->clear();
|
||||
} else {
|
||||
m_secrets = std::make_unique<Io::PasswordFile>();
|
||||
}
|
||||
m_secrets->setPath(argsToString("secrets/"sv, userAuth.name));
|
||||
m_secrets->setPassword(userAuth.password.data(), userAuth.password.size());
|
||||
} catch (const std::ios_base::failure &e) {
|
||||
cerr << Phrases::WarningMessage << "Failed to close password file \"" << m_secrets.path() << "\" (before preparing new one): " << e.what() << Phrases::End;
|
||||
cerr << Phrases::WarningMessage << "Failed to close password file \"" << m_secrets->path()
|
||||
<< "\" (before preparing new one): " << e.what() << Phrases::End;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,10 +131,12 @@ void Session::received(boost::system::error_code ec, size_t bytesTransferred)
|
|||
|
||||
// discard password; secrets are expected to be read on the immediate call of the route
|
||||
try {
|
||||
m_secrets.clearPassword();
|
||||
m_secrets.close();
|
||||
if (m_secrets) {
|
||||
m_secrets->clearPassword();
|
||||
m_secrets->close();
|
||||
}
|
||||
} catch (const std::ios_base::failure &e) {
|
||||
cerr << Phrases::WarningMessage << "Failed to close password file \"" << m_secrets.path() << "\": " << e.what() << Phrases::End;
|
||||
cerr << Phrases::WarningMessage << "Failed to close password file \"" << m_secrets->path() << "\": " << e.what() << Phrases::End;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "./typedefs.h"
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
#include "../global.h"
|
||||
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
|
@ -12,15 +12,22 @@
|
|||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Io {
|
||||
class PasswordFile;
|
||||
}
|
||||
|
||||
namespace LibRepoMgr {
|
||||
|
||||
struct ServiceSetup;
|
||||
|
||||
namespace WebAPI {
|
||||
|
||||
class Session : public std::enable_shared_from_this<Session> {
|
||||
class LIBREPOMGR_EXPORT Session : public std::enable_shared_from_this<Session> {
|
||||
public:
|
||||
Session(boost::asio::ip::tcp::socket &&socket, ServiceSetup &config);
|
||||
~Session();
|
||||
|
||||
void receive();
|
||||
void respond(std::shared_ptr<Response> &&response);
|
||||
|
@ -33,7 +40,7 @@ public:
|
|||
void received(boost::system::error_code ec, std::size_t bytesTransferred);
|
||||
void responded(boost::system::error_code ec, std::size_t bytesTransferred, bool shouldClose);
|
||||
static boost::beast::string_view determineMimeType(std::string_view path, boost::beast::string_view fallback = "text/plain");
|
||||
Io::PasswordFile &secrets();
|
||||
std::unique_ptr<Io::PasswordFile> &&secrets();
|
||||
|
||||
private:
|
||||
boost::asio::ip::tcp::socket m_socket;
|
||||
|
@ -42,16 +49,9 @@ private:
|
|||
std::unique_ptr<RequestParser> m_parser;
|
||||
ServiceSetup &m_setup;
|
||||
std::shared_ptr<void> m_res;
|
||||
Io::PasswordFile m_secrets;
|
||||
std::unique_ptr<Io::PasswordFile> m_secrets;
|
||||
};
|
||||
|
||||
inline Session::Session(boost::asio::ip::tcp::socket &&socket, ServiceSetup &setup)
|
||||
: m_socket(std::move(socket))
|
||||
, m_strand(m_socket.get_executor())
|
||||
, m_setup(setup)
|
||||
{
|
||||
}
|
||||
|
||||
inline const Request &Session::request() const
|
||||
{
|
||||
return m_parser->get();
|
||||
|
@ -67,9 +67,9 @@ inline boost::asio::ip::tcp::socket &Session::socket()
|
|||
return m_socket;
|
||||
}
|
||||
|
||||
inline Io::PasswordFile &Session::secrets()
|
||||
inline std::unique_ptr<Io::PasswordFile> &&Session::secrets()
|
||||
{
|
||||
return m_secrets;
|
||||
return std::move(m_secrets);
|
||||
}
|
||||
|
||||
} // namespace WebAPI
|
||||
|
|
Loading…
Reference in New Issue