Beispiel #1
0
StatusWith<StringData> ClientMetadata::parseApplicationDocument(const BSONObj& doc) {
    BSONObjIterator i(doc);

    while (i.more()) {
        BSONElement e = i.next();
        StringData name = e.fieldNameStringData();

        // Name is the only required field, and any other fields are simply ignored.
        if (name == kName) {

            if (e.type() != String) {
                return {
                    ErrorCodes::TypeMismatch,
                    str::stream() << "The '" << kApplication << "." << kName
                                  << "' field must be a string in the client metadata document"};
            }

            StringData value = e.checkAndGetStringData();

            if (value.size() > kMaxApplicationNameByteLength) {
                return {ErrorCodes::ClientMetadataAppNameTooLarge,
                        str::stream() << "The '" << kApplication << "." << kName
                                      << "' field must be less then or equal to "
                                      << kMaxApplicationNameByteLength
                                      << " bytes in the client metadata document"};
            }

            return {std::move(value)};
        }
    }

    return {StringData()};
}
Beispiel #2
0
ApplyOpsValidity validateApplyOpsCommand(const BSONObj& cmdObj) {
    const size_t maxApplyOpsDepth = 10;
    std::stack<std::pair<size_t, BSONObj>> toCheck;

    auto operationContainsApplyOps = [](const BSONObj& opObj) {
        BSONElement opTypeElem = opObj["op"];
        checkBSONType(BSONType::String, opTypeElem);
        const StringData opType = opTypeElem.checkAndGetStringData();

        if (opType == "c"_sd) {
            BSONElement oElem = opObj["o"];
            checkBSONType(BSONType::Object, oElem);
            BSONObj o = oElem.Obj();

            if (o.firstElement().fieldNameStringData() == "applyOps"_sd) {
                return true;
            }
        }
        return false;
    };

    // Insert the top level applyOps command into the stack.
    toCheck.emplace(std::make_pair(0, cmdObj));

    while (!toCheck.empty()) {
        std::pair<size_t, BSONObj> item = toCheck.top();
        toCheck.pop();

        checkBSONType(BSONType::Array, item.second.firstElement());
        // Check if the applyOps command is empty. This is probably not something that should
        // happen, so require a superuser to do this.
        if (item.second.firstElement().Array().empty()) {
            return ApplyOpsValidity::kNeedsSuperuser;
        }

        // For each applyOps command, iterate the ops.
        for (BSONElement element : item.second.firstElement().Array()) {
            checkBSONType(BSONType::Object, element);
            BSONObj elementObj = element.Obj();

            // If the op itself contains an applyOps...
            if (operationContainsApplyOps(elementObj)) {
                // And we've recursed too far, then bail out.
                uassert(ErrorCodes::FailedToParse,
                        "Too many nested applyOps",
                        item.first < maxApplyOpsDepth);

                // Otherwise, if the op contains an applyOps, but we haven't recursed too far:
                // extract the applyOps command, and insert it into the stack.
                checkBSONType(BSONType::Object, elementObj["o"]);
                BSONObj oObj = elementObj["o"].Obj();
                toCheck.emplace(std::make_pair(item.first + 1, std::move(oObj)));
            }
        }
    }

    return ApplyOpsValidity::kOk;
}
Beispiel #3
0
Status checkAuthForApplyOpsCommand(OperationContext* txn,
                                   const std::string& dbname,
                                   const BSONObj& cmdObj) {
    AuthorizationSession* authSession = AuthorizationSession::get(txn->getClient());

    ApplyOpsValidity validity = validateApplyOpsCommand(cmdObj);
    if (validity == ApplyOpsValidity::kNeedsSuperuser) {
        std::vector<Privilege> universalPrivileges;
        RoleGraph::generateUniversalPrivileges(&universalPrivileges);
        if (!authSession->isAuthorizedForPrivileges(universalPrivileges)) {
            return Status(ErrorCodes::Unauthorized, "Unauthorized");
        }
        return Status::OK();
    }
    fassert(40314, validity == ApplyOpsValidity::kOk);

    boost::optional<DisableDocumentValidation> maybeDisableValidation;
    if (shouldBypassDocumentValidationForCommand(cmdObj))
        maybeDisableValidation.emplace(txn);


    const bool alwaysUpsert =
        cmdObj.hasField("alwaysUpsert") ? cmdObj["alwaysUpsert"].trueValue() : true;

    checkBSONType(BSONType::Array, cmdObj.firstElement());
    for (const BSONElement& e : cmdObj.firstElement().Array()) {
        checkBSONType(BSONType::Object, e);
        Status status = checkOperationAuthorization(txn, dbname, e.Obj(), alwaysUpsert);
        if (!status.isOK()) {
            return status;
        }
    }

    BSONElement preconditions = cmdObj["preCondition"];
    if (!preconditions.eoo()) {
        for (const BSONElement& precondition : preconditions.Array()) {
            checkBSONType(BSONType::Object, precondition);
            BSONElement nsElem = precondition.Obj()["ns"];
            checkBSONType(BSONType::String, nsElem);
            NamespaceString nss(nsElem.checkAndGetStringData());

            if (!authSession->isAuthorizedForActionsOnResource(
                    ResourcePattern::forExactNamespace(nss), ActionType::find)) {
                return Status(ErrorCodes::Unauthorized, "Unauthorized to check precondition");
            }
        }
    }

    return Status::OK();
}