diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 7687e50be..fe395d250 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -31,6 +31,7 @@ using namespace icinga; +thread_local std::function ConfigItem::m_OverrideRegistry; std::mutex ConfigItem::m_Mutex; ConfigItem::TypeMap ConfigItem::m_Items; ConfigItem::TypeMap ConfigItem::m_DefaultTemplates; @@ -261,41 +262,39 @@ 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); - { - std::unique_lock lock(m_Mutex); - m_IgnoredItems.push_back(m_DebugInfo.Path); + { + std::unique_lock lock(m_Mutex); + m_IgnoredItems.push_back(m_DebugInfo.Path); + } + + return nullptr; } - return nullptr; + throw; } - - throw; } - Value serializedObject; + if (!m_OverrideRegistry) { + Value serializedObject; - try { - if (ConfigCompilerContext::GetInstance()->IsOpen()) { + try { serializedObject = Serialize(dobj, FAConfig); - } else { - AssertNoCircularReferences(dobj); + } catch (const CircularReferenceError& ex) { + BOOST_THROW_EXCEPTION(ValidationError(dobj, ex.GetPath(), "Circular references are not allowed")); } - } catch (const CircularReferenceError& ex) { - BOOST_THROW_EXCEPTION(ValidationError(dobj, ex.GetPath(), "Circular references are not allowed")); - } - if (ConfigCompilerContext::GetInstance()->IsOpen()) { Dictionary::Ptr persistentItem = new Dictionary({ { "type", type->GetName() }, { "name", GetName() }, - { "properties", serializedObject }, + { "properties", Serialize(dobj, FAConfig) }, { "debug_hints", dhint }, { "debug_info", new Array({ m_DebugInfo.Path, @@ -306,13 +305,14 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) }) } }); + dhint.reset(); + ConfigCompilerContext::GetInstance()->WriteObject(persistentItem); + persistentItem.reset(); + + dobj->Register(); } - dhint.reset(); - - dobj->Register(); - m_Object = dobj; return dobj; @@ -323,6 +323,11 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) */ void ConfigItem::Register() { + if (m_OverrideRegistry) { + m_OverrideRegistry(this); + return; + } + m_ActivationContext = ActivationContext::GetCurrentContext(); std::unique_lock lock(m_Mutex); @@ -356,6 +361,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 bd04b47f4..322b75d15 100644 --- a/lib/config/configitem.hpp +++ b/lib/config/configitem.hpp @@ -9,6 +9,7 @@ #include "config/activationcontext.hpp" #include "base/configobject.hpp" #include "base/workqueue.hpp" +#include namespace icinga { @@ -44,6 +45,7 @@ public: void Register(); void Unregister(); + ConfigObject::Ptr Commit(bool discard = true); DebugInfo GetDebugInfo() const; Dictionary::Ptr GetScope() const; @@ -64,6 +66,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. */ @@ -97,8 +101,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 29d1d52b5..8ee2bfeb1 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -10,6 +10,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; @@ -42,7 +45,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"); @@ -134,6 +139,105 @@ bool CreateObjectHandler::HandleRequest( // Lock the object name of the given type to prevent from being created concurrently. ObjectNameLock objectNameLock(type, name); + { + 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);