From 13c4bb06b839f30f0c18290c72551c333cc18692 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 24 Mar 2016 09:15:09 +0100 Subject: [PATCH] Improve validation for arrays fixes #11434 --- lib/config/configitem.cpp | 34 ++++++------ lib/icinga/user.cpp | 2 + tools/mkclass/classcompiler.cpp | 95 ++++++++++++++++++++------------- 3 files changed, 76 insertions(+), 55 deletions(-) diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 8a14de5aa..83d40810b 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -226,6 +226,23 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) dobj->SetName(name); + Dictionary::Ptr dhint = debugHints.ToDictionary(); + + try { + DefaultValidationUtils utils; + dobj->Validate(FAConfig, utils); + } catch (ValidationError& ex) { + if (m_IgnoreOnError) { + Log(LogWarning, "ConfigObject") + << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex); + + return ConfigObject::Ptr(); + } + + ex.SetDebugHint(dhint); + throw; + } + try { dobj->OnConfigLoaded(); } catch (const std::exception& ex) { @@ -244,8 +261,6 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) persistentItem->Set("type", GetType()); persistentItem->Set("name", GetName()); persistentItem->Set("properties", Serialize(dobj, FAConfig)); - - Dictionary::Ptr dhint = debugHints.ToDictionary(); persistentItem->Set("debug_hints", dhint); Array::Ptr di = new Array(); @@ -256,21 +271,6 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) di->Add(m_DebugInfo.LastColumn); persistentItem->Set("debug_info", di); - try { - DefaultValidationUtils utils; - dobj->Validate(FAConfig, utils); - } catch (ValidationError& ex) { - if (m_IgnoreOnError) { - Log(LogWarning, "ConfigObject") - << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex); - - return ConfigObject::Ptr(); - } - - ex.SetDebugHint(dhint); - throw; - } - ConfigCompilerContext::GetInstance()->WriteObject(persistentItem); persistentItem.reset(); diff --git a/lib/icinga/user.cpp b/lib/icinga/user.cpp index f4e35d023..eb232fc84 100644 --- a/lib/icinga/user.cpp +++ b/lib/icinga/user.cpp @@ -111,6 +111,8 @@ void User::ValidateStates(const Array::Ptr& value, const ValidationUtils& utils) void User::ValidateTypes(const Array::Ptr& value, const ValidationUtils& utils) { + ObjectImpl::ValidateTypes(value, utils); + int tfilter = FilterArrayToInt(value, 0); if ((tfilter & ~(1 << NotificationDowntimeStart | 1 << NotificationDowntimeEnd | 1 << NotificationDowntimeRemoved | diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index 9914f0d3e..d0aab97a9 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -158,6 +158,33 @@ static bool FieldTypeCmp(const Field& a, const Field& b) return a.Type.GetRealType() < b.Type.GetRealType(); } +static std::string FieldTypeToIcingaName(const Field& field, bool inner) +{ + std::string ftype = field.Type.TypeName; + + if (!inner && field.Type.ArrayRank > 0) + return "Array"; + + if (field.Type.IsName) + return "String"; + + if (field.Attributes & FAEnum) + return "Number"; + + if (ftype == "bool" || ftype == "int" || ftype == "double") + return "Number"; + + if (ftype == "int" || ftype == "double") + return "Number"; + else if (ftype == "bool") + return "Boolean"; + + if (ftype.find("::Ptr") != std::string::npos) + return ftype.substr(0, ftype.size() - strlen("::Ptr")); + + return ftype; +} + void ClassCompiler::OptimizeStructLayout(std::vector& fields) { std::sort(fields.begin(), fields.end(), FieldTypeCmp); @@ -342,21 +369,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) size_t num = 0; for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { - std::string ftype = it->Type.GetRealType(); - - if (ftype == "bool" || ftype == "int" || ftype == "double") - ftype = "Number"; - - if (ftype == "int" || ftype == "double") - ftype = "Number"; - else if (ftype == "bool") - ftype = "Boolean"; - - if (ftype.find("::Ptr") != std::string::npos) - ftype = ftype.substr(0, ftype.size() - strlen("::Ptr")); - - if (it->Attributes & FAEnum) - ftype = "Number"; + std::string ftype = FieldTypeToIcingaName(*it, false); std::string nameref; @@ -487,33 +500,39 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) const Field& field = *it; - if ((field.Attributes & (FARequired)) || field.Type.IsName) { - if (field.Attributes & FARequired) { - if (field.Type.GetRealType().find("::Ptr") != std::string::npos) - m_Impl << "\t" << "if (!value)" << std::endl; - else - m_Impl << "\t" << "if (value.IsEmpty())" << std::endl; + if (field.Attributes & FARequired) { + if (field.Type.GetRealType().find("::Ptr") != std::string::npos) + m_Impl << "\t" << "if (!value)" << std::endl; + else + m_Impl << "\t" << "if (value.IsEmpty())" << std::endl; - m_Impl << "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast(this), boost::assign::list_of(\"" << field.Name << "\"), \"Attribute must not be empty.\"));" << std::endl << std::endl; - } + m_Impl << "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast(this), boost::assign::list_of(\"" << field.Name << "\"), \"Attribute must not be empty.\"));" << std::endl << std::endl; + } - if (field.Type.IsName) { - if (field.Type.ArrayRank > 0) { - m_Impl << "\t" << "if (value) {" << std::endl - << "\t\t" << "ObjectLock olock(value);" << std::endl - << "\t\t" << "BOOST_FOREACH(const String& ref, value) {" << std::endl; - } else - m_Impl << "\t" << "String ref = value;" << std::endl; + if (field.Type.ArrayRank > 0) { + m_Impl << "\t" << "if (value) {" << std::endl + << "\t\t" << "ObjectLock olock(value);" << std::endl + << "\t\t" << "BOOST_FOREACH(const Value& avalue, value) {" << std::endl; + } else + m_Impl << "\t" << "Value avalue = value;" << std::endl; - m_Impl << "\t" << "if (!ref.IsEmpty() && !utils.ValidateName(\"" << field.Type.TypeName << "\", ref))" << std::endl - << "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast(this), boost::assign::list_of(\"" << field.Name << "\"), \"Object '\" + ref + \"' of type '" << field.Type.TypeName - << "' does not exist.\"));" << std::endl; + std::string ftype = FieldTypeToIcingaName(field, true); - if (field.Type.ArrayRank > 0) { - m_Impl << "\t\t" << "}" << std::endl - << "\t" << "}" << std::endl; - } - } + if (field.Type.IsName) { + m_Impl << "\t" << "if (!avalue.IsEmpty() && !utils.ValidateName(\"" << field.Type.TypeName << "\", avalue))" << std::endl + << "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast(this), boost::assign::list_of(\"" << field.Name << "\"), \"Object '\" + avalue + \"' of type '" << field.Type.TypeName + << "' does not exist.\"));" << std::endl; + } else if (field.Type.ArrayRank > 0 && (ftype == "Number" || ftype == "Boolean")) { + m_Impl << "\t" << "try {" << std::endl + << "\t\t" << "Convert::ToDouble(avalue);" << std::endl + << "\t" << "} catch (const std::invalid_argument&) {" << std::endl + << "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast(this), boost::assign::list_of(\"" << field.Name << "\"), \"Array element '\" + avalue + \"' of type '\" + avalue.GetReflectionType()->GetName() + \"' is not valid here; expected type '" << ftype << "'.\"));" << std::endl + << "\t" << "}" << std::endl; + } + + if (field.Type.ArrayRank > 0) { + m_Impl << "\t\t" << "}" << std::endl + << "\t" << "}" << std::endl; } m_Impl << "}" << std::endl << std::endl;