diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 6dbb41f59..9d7ec001e 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -28,6 +28,7 @@ using namespace icinga; +thread_local std::function ConfigItem::m_OverrideRegistry; boost::mutex ConfigItem::m_Mutex; ConfigItem::TypeMap ConfigItem::m_Items; ConfigItem::TypeMap ConfigItem::m_DefaultTemplates; @@ -258,53 +259,57 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) throw; } - try { - dobj->OnConfigLoaded(); - } catch (const std::exception& ex) { - if (m_IgnoreOnError) { - Log(LogNotice, "ConfigObject") - << "Ignoring config object '" << m_Name << "' of type '" << m_Type->GetName() << "' due to errors: " << DiagnosticInformation(ex); + if (!m_OverrideRegistry) { + try { + dobj->OnConfigLoaded(); + } catch (const std::exception& ex) { + if (m_IgnoreOnError) { + Log(LogNotice, "ConfigObject") + << "Ignoring config object '" << m_Name << "' of type '" << m_Type->GetName() << "' due to errors: " << DiagnosticInformation(ex); - { - boost::mutex::scoped_lock lock(m_Mutex); - m_IgnoredItems.push_back(m_DebugInfo.Path); + { + boost::mutex::scoped_lock lock(m_Mutex); + m_IgnoredItems.push_back(m_DebugInfo.Path); + } + + return nullptr; } - return nullptr; + throw; + } + } + + if (!m_OverrideRegistry) { + Value serializedObject; + + try { + serializedObject = Serialize(dobj, FAConfig); + } catch (const CircularReferenceError& ex) { + BOOST_THROW_EXCEPTION(ValidationError(dobj, ex.GetPath(), "Circular references are not allowed")); } - throw; + Dictionary::Ptr persistentItem = new Dictionary({ + { "type", type->GetName() }, + { "name", GetName() }, + { "properties", Serialize(dobj, FAConfig) }, + { "debug_hints", dhint }, + { "debug_info", new Array({ + m_DebugInfo.Path, + m_DebugInfo.FirstLine, + m_DebugInfo.FirstColumn, + m_DebugInfo.LastLine, + m_DebugInfo.LastColumn, + }) } + }); + + dhint.reset(); + + ConfigCompilerContext::GetInstance()->WriteObject(persistentItem); + persistentItem.reset(); + + dobj->Register(); } - Value serializedObject; - - try { - serializedObject = Serialize(dobj, FAConfig); - } catch (const CircularReferenceError& ex) { - BOOST_THROW_EXCEPTION(ValidationError(dobj, ex.GetPath(), "Circular references are not allowed")); - } - - Dictionary::Ptr persistentItem = new Dictionary({ - { "type", type->GetName() }, - { "name", GetName() }, - { "properties", Serialize(dobj, FAConfig) }, - { "debug_hints", dhint }, - { "debug_info", new Array({ - m_DebugInfo.Path, - m_DebugInfo.FirstLine, - m_DebugInfo.FirstColumn, - m_DebugInfo.LastLine, - m_DebugInfo.LastColumn, - }) } - }); - - dhint.reset(); - - ConfigCompilerContext::GetInstance()->WriteObject(persistentItem); - persistentItem.reset(); - - dobj->Register(); - m_Object = dobj; return dobj; @@ -315,6 +320,11 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) */ void ConfigItem::Register() { + if (m_OverrideRegistry) { + m_OverrideRegistry(this); + return; + } + m_ActivationContext = ActivationContext::GetCurrentContext(); boost::mutex::scoped_lock lock(m_Mutex); @@ -348,6 +358,10 @@ void ConfigItem::Register() */ void ConfigItem::Unregister() { + if (m_OverrideRegistry) { + return; + } + if (m_Object) { m_Object->Unregister(); m_Object.reset(); diff --git a/lib/config/configitem.hpp b/lib/config/configitem.hpp index 7b765491d..32ff7914f 100644 --- a/lib/config/configitem.hpp +++ b/lib/config/configitem.hpp @@ -8,6 +8,7 @@ #include "config/activationcontext.hpp" #include "base/configobject.hpp" #include "base/workqueue.hpp" +#include namespace icinga { @@ -43,6 +44,7 @@ public: void Register(); void Unregister(); + ConfigObject::Ptr Commit(bool discard = true); DebugInfo GetDebugInfo() const; Dictionary::Ptr GetScope() const; @@ -63,6 +65,8 @@ public: static void RemoveIgnoredItems(const String& allowedConfigPath); + static thread_local std::function m_OverrideRegistry; + private: Type::Ptr m_Type; /**< The object type. */ String m_Name; /**< The name. */ @@ -96,8 +100,6 @@ private: static ConfigItem::Ptr GetObjectUnlocked(const String& type, const String& name); - ConfigObject::Ptr Commit(bool discard = true); - static bool CommitNewItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector& newItems); }; diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index c01b23641..cfb94e6c1 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -8,6 +8,9 @@ #include "remote/apiaction.hpp" #include "remote/zone.hpp" #include "base/configtype.hpp" +#include "base/defer.hpp" +#include "config/configcompiler.hpp" +#include "config/configitem.hpp" #include using namespace icinga; @@ -40,7 +43,9 @@ bool CreateObjectHandler::HandleRequest( return true; } - FilterUtility::CheckPermission(user, "objects/create/" + type->GetName()); + auto requiredPermission ("objects/create/" + type->GetName()); + + FilterUtility::CheckPermission(user, requiredPermission); String name = url->GetPath()[3]; Array::Ptr templates = params->Get("templates"); @@ -116,6 +121,105 @@ bool CreateObjectHandler::HandleRequest( return true; } + { + auto permissions (user->GetPermissions()); + + if (permissions) { + std::unique_ptr filters; + + { + ObjectLock oLock (permissions); + + for (auto& item : permissions) { + Function::Ptr filter; + + if (item.IsObjectType()) { + Dictionary::Ptr dict = item; + filter = dict->Get("filter"); + + if (Utility::Match(dict->Get("permission"), requiredPermission)) { + if (!filter) { + filters.reset(); + break; + } + + std::vector> args; + args.emplace_back(new GetScopeExpression(ScopeThis)); + + std::unique_ptr indexer = (std::unique_ptr)new IndexerExpression( + std::unique_ptr(MakeLiteral(filter)), + std::unique_ptr(MakeLiteral("call")) + ); + + std::unique_ptr fexpr = (std::unique_ptr)new FunctionCallExpression( + std::move(indexer), std::move(args) + ); + + if (filters) { + filters = (std::unique_ptr)new LogicalOrExpression( + std::move(filters), std::move(fexpr) + ); + } else { + filters = std::move(fexpr); + } + } + } else if (Utility::Match(item, requiredPermission)) { + filters.reset(); + break; + } + } + } + + if (filters) { + try { + auto expr (ConfigCompiler::CompileText("", config, "", "_api")); + + ConfigItem::Ptr item; + ConfigItem::m_OverrideRegistry = [&item](ConfigItem *ci) { item = ci; }; + + Defer overrideRegistry ([]() { + ConfigItem::m_OverrideRegistry = decltype(ConfigItem::m_OverrideRegistry)(); + }); + + ActivationScope ascope; + + { + ScriptFrame frame(true); + expr->Evaluate(frame); + } + + expr.reset(); + + if (item) { + auto obj (item->Commit(false)); + + if (obj) { + ScriptFrame frame (false, new Namespace()); + + if (!FilterUtility::EvaluateFilter(frame, filters.get(), obj)) { + BOOST_THROW_EXCEPTION(ScriptError( + "Access denied to object '" + name + "' of type '" + type->GetName() + "'" + )); + } + } + } + } catch (const std::exception& ex) { + result1->Set("errors", new Array({ ex.what() })); + result1->Set("code", 500); + result1->Set("status", "Object could not be created."); + + if (verbose) + result1->Set("diagnostic_information", DiagnosticInformation(ex)); + + response.result(http::status::internal_server_error); + HttpUtility::SendJsonBody(response, params, result); + + return true; + } + } + } + } + if (!ConfigObjectUtility::CreateObject(type, name, config, errors, diagnosticInformation)) { result1->Set("errors", errors); result1->Set("code", 500);