/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once #include #include #include "mongo/base/string_data.h" #include "mongo/bson/bsonelement.h" #include "mongo/bson/bsontypes.h" #include "mongo/db/namespace_string.h" namespace mongo { /** * IDLParserErrorContext manages the current parser context for parsing BSON documents. * * The class stores the path to the current document to enable it provide more useful error * messages. The path is a dot delimited list of field names which is useful for nested struct * parsing. * * This class is responsible for throwing all error messages the IDL generated parsers throw, * and provide utility methods like checking a BSON type or set of BSON types. */ class IDLParserErrorContext { IDLParserErrorContext(const IDLParserErrorContext&) = delete; IDLParserErrorContext& operator=(const IDLParserErrorContext&) = delete; template friend void throwComparisonError(IDLParserErrorContext& ctxt, StringData fieldName, StringData op, T actualValue, T expectedValue); public: /** * String constants for well-known IDL fields. */ static constexpr auto kOpMsgDollarDB = "$db"_sd; static constexpr auto kOpMsgDollarDBDefault = "admin"_sd; IDLParserErrorContext(StringData fieldName) : _currentField(fieldName), _predecessor(nullptr) {} IDLParserErrorContext(StringData fieldName, const IDLParserErrorContext* predecessor) : _currentField(fieldName), _predecessor(predecessor) {} /** * Check that BSON element is a given type or whether the field should be skipped. * * Returns true if the BSON element is the correct type. * Return false if the BSON element is Null or Undefined and the field's value should not be * processed. * Throws an exception if the BSON element's type is wrong. */ bool checkAndAssertType(const BSONElement& element, BSONType type) const { if (MONGO_likely(element.type() == type)) { return true; } return checkAndAssertTypeSlowPath(element, type); } /** * Check that BSON element is a bin data type, and has the specified bin data subtype, or * whether the field should be skipped. * * Returns true if the BSON element is the correct type. * Return false if the BSON element is Null or Undefined and the field's value should not be * processed. * Throws an exception if the BSON element's type is wrong. */ bool checkAndAssertBinDataType(const BSONElement& element, BinDataType type) const { if (MONGO_likely(element.type() == BinData && element.binDataType() == type)) { return true; } return checkAndAssertBinDataTypeSlowPath(element, type); } /** * Check that BSON element is one of a given type or whether the field should be skipped. * * Returns true if the BSON element is one of the types. * Return false if the BSON element is Null or Undefined and the field's value should not be * processed. * Throws an exception if the BSON element's type is wrong. */ bool checkAndAssertTypes(const BSONElement& element, const std::vector& types) const; /** * Throw an error message about the BSONElement being a duplicate field. */ MONGO_COMPILER_NORETURN void throwDuplicateField(const BSONElement& element) const; /** * Throw an error message about the BSONElement being a duplicate field. */ MONGO_COMPILER_NORETURN void throwDuplicateField(StringData fieldName) const; /** * Throw an error message about the required field missing from the document. */ MONGO_COMPILER_NORETURN void throwMissingField(StringData fieldName) const; /** * Throw an error message about an unknown field in a document. */ MONGO_COMPILER_NORETURN void throwUnknownField(StringData fieldName) const; /** * Throw an error message about an array field name not being a valid unsigned integer. */ MONGO_COMPILER_NORETURN void throwBadArrayFieldNumberValue(StringData value) const; /** * Throw an error message about the array field name not being the next number in the sequence. */ MONGO_COMPILER_NORETURN void throwBadArrayFieldNumberSequence( std::uint32_t actualValue, std::uint32_t expectedValue) const; /** * Throw an error message about an unrecognized enum value. */ MONGO_COMPILER_NORETURN void throwBadEnumValue(StringData enumValue) const; MONGO_COMPILER_NORETURN void throwBadEnumValue(int enumValue) const; /** * Equivalent to CommandHelpers::parseNsCollectionRequired */ static NamespaceString parseNSCollectionRequired(StringData dbName, const BSONElement& element); /** * Equivalent to CommandHelpers::parseNsOrUUID */ static NamespaceStringOrUUID parseNsOrUUID(StringData dbname, const BSONElement& element); /** * Take all the well known command generic arguments from commandPassthroughFields, but ignore * fields that are already part of the command and append the rest to builder. */ static void appendGenericCommandArguments(const BSONObj& commandPassthroughFields, const std::vector& knownFields, BSONObjBuilder* builder); private: /** * See comment on getElementPath below. */ std::string getElementPath(const BSONElement& element) const; /** * Return a dot seperated path to the specified field. For instance, if the code is parsing a * grandchild field that has an error, this will return "grandparent.parent.child". */ std::string getElementPath(StringData fieldName) const; /** * See comment on checkAndAssertType. */ bool checkAndAssertTypeSlowPath(const BSONElement& element, BSONType type) const; /** * See comment on checkAndAssertBinDataType. */ bool checkAndAssertBinDataTypeSlowPath(const BSONElement& element, BinDataType type) const; private: // Name of the current field that is being parsed. const StringData _currentField; // Pointer to a parent parser context. // This provides a singly linked list of parent pointers, and use to produce a full path to a // field with an error. const IDLParserErrorContext* _predecessor; }; /** * Throw an error when BSON validation fails during parse. */ template void throwComparisonError(IDLParserErrorContext& ctxt, StringData fieldName, StringData op, T actualValue, T expectedValue) { std::string path = ctxt.getElementPath(fieldName); throwComparisonError(path, op, actualValue, expectedValue); } /** * Throw an error when a user calls a setter and it fails the comparison. */ template void throwComparisonError(StringData fieldName, StringData op, T actualValue, T expectedValue) { uasserted(51024, str::stream() << "BSON field '" << fieldName << "' value must be " << op << " " << expectedValue << ", actual value '" << actualValue << "'"); } /** * Transform a vector of input type to a vector of output type. * * Used by the IDL generated code to transform between vectors of view, and non-view types. */ std::vector transformVector(const std::vector& input); std::vector transformVector(const std::vector& input); std::vector transformVector(const std::vector>& input); std::vector> transformVector(const std::vector& input); } // namespace mongo