Merge pull request #10716 from Icinga/drop-thread-local-variable-apiuser
Some checks failed
Container Image / Container Image (push) Has been cancelled
Linux / alpine:bash (push) Has been cancelled
Linux / amazonlinux:2 (push) Has been cancelled
Linux / amazonlinux:2023 (push) Has been cancelled
Linux / debian:11 (linux/386) (push) Has been cancelled
Linux / debian:11 (push) Has been cancelled
Linux / debian:12 (linux/386) (push) Has been cancelled
Linux / debian:12 (push) Has been cancelled
Linux / debian:13 (push) Has been cancelled
Linux / fedora:41 (push) Has been cancelled
Linux / fedora:42 (push) Has been cancelled
Linux / fedora:43 (push) Has been cancelled
Linux / opensuse/leap:15.6 (push) Has been cancelled
Linux / opensuse/leap:16.0 (push) Has been cancelled
Linux / registry.suse.com/bci/bci-base:16.0 (push) Has been cancelled
Linux / registry.suse.com/suse/sle15:15.6 (push) Has been cancelled
Linux / registry.suse.com/suse/sle15:15.7 (push) Has been cancelled
Linux / rockylinux/rockylinux:10 (push) Has been cancelled
Linux / rockylinux:8 (push) Has been cancelled
Linux / rockylinux:9 (push) Has been cancelled
Linux / ubuntu:22.04 (push) Has been cancelled
Linux / ubuntu:24.04 (push) Has been cancelled
Linux / ubuntu:25.04 (push) Has been cancelled
Linux / ubuntu:25.10 (push) Has been cancelled
Windows / Windows (push) Has been cancelled

Remove `AuthenticatedApiUser` thread-local variable & pass it as arg instead
This commit is contained in:
Julian Brost 2026-02-13 14:43:36 +01:00 committed by GitHub
commit d02cdda5e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 94 additions and 63 deletions

View file

@ -54,8 +54,11 @@ Dictionary::Ptr ApiActions::CreateResult(int code, const String& status,
return result;
}
Dictionary::Ptr ApiActions::ProcessCheckResult(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::ProcessCheckResult(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
using Result = Checkable::ProcessingResult;
@ -141,8 +144,11 @@ Dictionary::Ptr ApiActions::ProcessCheckResult(const ConfigObject::Ptr& object,
return ApiActions::CreateResult(500, "Unexpected result (" + std::to_string(static_cast<int>(result)) + ") for object '" + checkable->GetName() + "'. Please submit a bug report at https://github.com/Icinga/icinga2");
}
Dictionary::Ptr ApiActions::RescheduleCheck(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::RescheduleCheck(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
@ -166,8 +172,11 @@ Dictionary::Ptr ApiActions::RescheduleCheck(const ConfigObject::Ptr& object,
return ApiActions::CreateResult(200, "Successfully rescheduled check for object '" + checkable->GetName() + "'.");
}
Dictionary::Ptr ApiActions::SendCustomNotification(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::SendCustomNotification(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
@ -189,8 +198,11 @@ Dictionary::Ptr ApiActions::SendCustomNotification(const ConfigObject::Ptr& obje
return ApiActions::CreateResult(200, "Successfully sent custom notification for object '" + checkable->GetName() + "'.");
}
Dictionary::Ptr ApiActions::DelayNotification(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::DelayNotification(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
@ -207,8 +219,11 @@ Dictionary::Ptr ApiActions::DelayNotification(const ConfigObject::Ptr& object,
return ApiActions::CreateResult(200, "Successfully delayed notifications for object '" + checkable->GetName() + "'.");
}
Dictionary::Ptr ApiActions::AcknowledgeProblem(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::AcknowledgeProblem(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
@ -269,8 +284,11 @@ Dictionary::Ptr ApiActions::AcknowledgeProblem(const ConfigObject::Ptr& object,
return ApiActions::CreateResult(200, "Successfully acknowledged problem for object '" + checkable->GetName() + "'.");
}
Dictionary::Ptr ApiActions::RemoveAcknowledgement(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::RemoveAcknowledgement(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
@ -293,8 +311,11 @@ Dictionary::Ptr ApiActions::RemoveAcknowledgement(const ConfigObject::Ptr& objec
return ApiActions::CreateResult(200, "Successfully removed acknowledgement for object '" + checkable->GetName() + "'.");
}
Dictionary::Ptr ApiActions::AddComment(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::AddComment(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
@ -332,8 +353,11 @@ Dictionary::Ptr ApiActions::AddComment(const ConfigObject::Ptr& object,
+ "'.", additional);
}
Dictionary::Ptr ApiActions::RemoveComment(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::RemoveComment(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
ConfigObjectsSharedLock lock (std::try_to_lock);
@ -366,8 +390,11 @@ Dictionary::Ptr ApiActions::RemoveComment(const ConfigObject::Ptr& object,
return ApiActions::CreateResult(200, "Successfully removed comment '" + commentName + "'.");
}
Dictionary::Ptr ApiActions::ScheduleDowntime(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::ScheduleDowntime(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
@ -536,8 +563,11 @@ Dictionary::Ptr ApiActions::ScheduleDowntime(const ConfigObject::Ptr& object,
downtimeName + "' for object '" + checkable->GetName() + "'.", additional);
}
Dictionary::Ptr ApiActions::RemoveDowntime(const ConfigObject::Ptr& object,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::RemoveDowntime(
const ConfigObject::Ptr& object,
const ApiUser::Ptr&,
const Dictionary::Ptr& params
)
{
ConfigObjectsSharedLock lock (std::try_to_lock);
@ -589,24 +619,29 @@ Dictionary::Ptr ApiActions::RemoveDowntime(const ConfigObject::Ptr& object,
}
}
Dictionary::Ptr ApiActions::ShutdownProcess(const ConfigObject::Ptr&,
[[maybe_unused]] const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::ShutdownProcess(
const ConfigObject::Ptr&,
const ApiUser::Ptr&,
[[maybe_unused]] const Dictionary::Ptr& params
)
{
Application::RequestShutdown();
return ApiActions::CreateResult(200, "Shutting down Icinga 2.");
}
Dictionary::Ptr ApiActions::RestartProcess(const ConfigObject::Ptr&,
[[maybe_unused]] const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::RestartProcess(
const ConfigObject::Ptr&,
const ApiUser::Ptr&,
[[maybe_unused]] const Dictionary::Ptr& params
)
{
Application::RequestRestart();
return ApiActions::CreateResult(200, "Restarting Icinga 2.");
}
Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&,
const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, const ApiUser::Ptr&, const Dictionary::Ptr& params)
{
if (!params->Contains("cn"))
return ApiActions::CreateResult(400, "Option 'cn' is required");
@ -654,7 +689,11 @@ Value ApiActions::GetSingleObjectByNameUsingPermissions(const String& type, cons
return objs.at(0);
};
Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params)
Dictionary::Ptr ApiActions::ExecuteCommand(
const ConfigObject::Ptr& object,
const ApiUser::Ptr& apiUser,
const Dictionary::Ptr& params
)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
@ -718,11 +757,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons
nullptr, MacroProcessor::EscapeCallback(), nullptr, false
);
if (!ActionsHandler::AuthenticatedApiUser)
if (!apiUser)
BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user."));
/* Get endpoint */
Endpoint::Ptr endpointPtr = GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, ActionsHandler::AuthenticatedApiUser);
Endpoint::Ptr endpointPtr = GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, apiUser);
if (!endpointPtr)
return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'.");
@ -780,7 +819,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons
Dictionary::Ptr execParams = new Dictionary();
if (command_type == "CheckCommand") {
CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser);
CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, apiUser);
if (!cmd)
return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'.");
@ -792,7 +831,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons
cmd->Execute(checkable, cr, listener->GetWaitGroup(), execMacros, false);
}
} else if (command_type == "EventCommand") {
EventCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser);
EventCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, apiUser);
if (!cmd)
return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'.");
@ -804,7 +843,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons
cmd->Execute(checkable, execMacros, false);
}
} else if (command_type == "NotificationCommand") {
NotificationCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser);
NotificationCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, apiUser);
if (!cmd)
return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'.");
@ -821,7 +860,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons
MacroProcessor::EscapeCallback(), nullptr, false
);
User::Ptr user = GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_user, ActionsHandler::AuthenticatedApiUser);
User::Ptr user = GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_user, apiUser);
if (!user)
return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'.");
@ -840,7 +879,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons
MacroProcessor::EscapeCallback(), nullptr, false
);
Notification::Ptr notification = GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_notification, ActionsHandler::AuthenticatedApiUser);
Notification::Ptr notification = GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_notification, apiUser);
if (!notification)
return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'.");
@ -853,7 +892,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons
});
cmd->Execute(notification, user, cr, NotificationType::NotificationCustom,
ActionsHandler::AuthenticatedApiUser->GetName(), "", execMacros, false);
apiUser->GetName(), "", execMacros, false);
}
}

View file

@ -18,20 +18,20 @@ namespace icinga
class ApiActions
{
public:
static Dictionary::Ptr ProcessCheckResult(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr RescheduleCheck(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr SendCustomNotification(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr DelayNotification(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr AcknowledgeProblem(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr RemoveAcknowledgement(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr AddComment(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr RemoveComment(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr ScheduleDowntime(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr RemoveDowntime(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr ShutdownProcess(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr RestartProcess(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr GenerateTicket(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params);
static Dictionary::Ptr ProcessCheckResult(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr RescheduleCheck(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr SendCustomNotification(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr DelayNotification(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr AcknowledgeProblem(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr RemoveAcknowledgement(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr AddComment(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr RemoveComment(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr ScheduleDowntime(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr RemoveDowntime(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr ShutdownProcess(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr RestartProcess(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr GenerateTicket(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
static Dictionary::Ptr ExecuteCommand(const ConfigObject::Ptr& object, const ApiUser::Ptr& apiUser, const Dictionary::Ptr& params);
private:
static Dictionary::Ptr CreateResult(int code, const String& status, const Dictionary::Ptr& additional = nullptr);

View file

@ -12,8 +12,6 @@
using namespace icinga;
thread_local ApiUser::Ptr ActionsHandler::AuthenticatedApiUser;
REGISTER_URLHANDLER("/v1/actions", ActionsHandler);
bool ActionsHandler::HandleRequest(
@ -80,11 +78,6 @@ bool ActionsHandler::HandleRequest(
bool verbose = false;
ActionsHandler::AuthenticatedApiUser = user;
Defer a ([]() {
ActionsHandler::AuthenticatedApiUser = nullptr;
});
if (params)
verbose = HttpUtility::GetLastParameter(params, "verbose");
@ -111,7 +104,7 @@ bool ActionsHandler::HandleRequest(
}
try {
results.emplace_back(action->Invoke(obj, params));
results.emplace_back(action->Invoke(obj, user, params));
} catch (const std::exception& ex) {
Dictionary::Ptr fail = new Dictionary({
{ "code", 500 },

View file

@ -14,8 +14,6 @@ class ActionsHandler final : public HttpHandler
public:
DECLARE_PTR_TYPEDEFS(ActionsHandler);
static thread_local ApiUser::Ptr AuthenticatedApiUser;
bool HandleRequest(
const WaitGroup::Ptr& waitGroup,
const HttpApiRequest& request,

View file

@ -13,9 +13,9 @@ ApiAction::ApiAction(std::vector<String> types, Callback action)
: m_Types(std::move(types)), m_Callback(std::move(action))
{ }
Value ApiAction::Invoke(const ConfigObject::Ptr& target, const Dictionary::Ptr& params)
Value ApiAction::Invoke(const ConfigObject::Ptr& target, const ApiUser::Ptr& user, const Dictionary::Ptr& params)
{
return m_Callback(target, params);
return m_Callback(target, user, params);
}
const std::vector<String>& ApiAction::GetTypes() const

View file

@ -9,6 +9,7 @@
#include "base/value.hpp"
#include "base/dictionary.hpp"
#include "base/configobject.hpp"
#include "remote/apiuser.hpp"
#include <vector>
#include <boost/algorithm/string/replace.hpp>
@ -25,11 +26,11 @@ class ApiAction final : public Object
public:
DECLARE_PTR_TYPEDEFS(ApiAction);
typedef std::function<Value(const ConfigObject::Ptr& target, const Dictionary::Ptr& params)> Callback;
typedef std::function<Value(const ConfigObject::Ptr& target, const ApiUser::Ptr&, const Dictionary::Ptr& params)> Callback;
ApiAction(std::vector<String> registerTypes, Callback function);
Value Invoke(const ConfigObject::Ptr& target, const Dictionary::Ptr& params);
Value Invoke(const ConfigObject::Ptr& target, const ApiUser::Ptr& user, const Dictionary::Ptr& params);
const std::vector<String>& GetTypes() const;