void Pipeline::addRequiredPrivileges(Command* commandTemplate, const string& db, BSONObj cmdObj, vector<Privilege>* out) { ResourcePattern inputResource(commandTemplate->parseResourcePattern(db, cmdObj)); uassert(17138, mongoutils::str::stream() << "Invalid input resource, " << inputResource.toString(), inputResource.isExactNamespacePattern()); out->push_back(Privilege(inputResource, ActionType::find)); BSONObj pipeline = cmdObj.getObjectField("pipeline"); BSONForEach(stageElem, pipeline) { BSONObj stage = stageElem.embeddedObjectUserCheck(); if (str::equals(stage.firstElementFieldName(), "$out")) { NamespaceString outputNs(db, stage.firstElement().str()); uassert(17139, mongoutils::str::stream() << "Invalid $out target namespace, " << outputNs.ns(), outputNs.isValid()); ActionSet actions; actions.addAction(ActionType::remove); actions.addAction(ActionType::insert); if (shouldBypassDocumentValidationForCommand(cmdObj)) { actions.addAction(ActionType::bypassDocumentValidation); } out->push_back(Privilege(ResourcePattern::forExactNamespace(outputNs), actions)); } }
Status checkAuthForCopydbCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { bool fromSelf = StringData(cmdObj.getStringField("fromhost")).empty(); StringData fromdb = cmdObj.getStringField("fromdb"); StringData todb = cmdObj.getStringField("todb"); // get system collections std::vector<std::string> legalClientSystemCollections; legalClientSystemCollections.push_back("system.js"); if (fromdb == "admin") { legalClientSystemCollections.push_back("system.users"); legalClientSystemCollections.push_back("system.roles"); legalClientSystemCollections.push_back("system.version"); } else if (fromdb == "local") { // TODO(spencer): shouldn't be possible. See SERVER-11383 legalClientSystemCollections.push_back("system.replset"); } // Check authorization on destination db ActionSet actions; actions.addAction(ActionType::insert); actions.addAction(ActionType::createIndex); if (shouldBypassDocumentValidationForCommand(cmdObj)) { actions.addAction(ActionType::bypassDocumentValidation); } if (!AuthorizationSession::get(client) ->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(todb), actions)) { return Status(ErrorCodes::Unauthorized, "Unauthorized"); } actions.removeAllActions(); actions.addAction(ActionType::insert); for (size_t i = 0; i < legalClientSystemCollections.size(); ++i) { if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnNamespace( NamespaceString(todb, legalClientSystemCollections[i]), actions)) { return Status(ErrorCodes::Unauthorized, "Unauthorized"); } } if (fromSelf) { // If copying from self, also require privileges on source db actions.removeAllActions(); actions.addAction(ActionType::find); if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(fromdb), actions)) { return Status(ErrorCodes::Unauthorized, "Unauthorized"); } for (size_t i = 0; i < legalClientSystemCollections.size(); ++i) { if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnNamespace( NamespaceString(fromdb, legalClientSystemCollections[i]), actions)) { return Status(ErrorCodes::Unauthorized, "Unauthorized"); } } } return Status::OK(); }
Status Pipeline::checkAuthForCommand(ClientBasic* client, const std::string& db, const BSONObj& cmdObj) { NamespaceString inputNs(db, cmdObj.firstElement().str()); auto inputResource = ResourcePattern::forExactNamespace(inputNs); uassert(17138, mongoutils::str::stream() << "Invalid input namespace, " << inputNs.ns(), inputNs.isValid()); std::vector<Privilege> privileges; if (dps::extractElementAtPath(cmdObj, "pipeline.0.$indexStats")) { Privilege::addPrivilegeToPrivilegeVector( &privileges, Privilege(ResourcePattern::forAnyNormalResource(), ActionType::indexStats)); } else if (dps::extractElementAtPath(cmdObj, "pipeline.0.$collStats")) { Privilege::addPrivilegeToPrivilegeVector(&privileges, Privilege(inputResource, ActionType::collStats)); } else { // If no source requiring an alternative permission scheme is specified then default to // requiring find() privileges on the given namespace. Privilege::addPrivilegeToPrivilegeVector(&privileges, Privilege(inputResource, ActionType::find)); } BSONObj pipeline = cmdObj.getObjectField("pipeline"); BSONForEach(stageElem, pipeline) { BSONObj stage = stageElem.embeddedObjectUserCheck(); StringData stageName = stage.firstElementFieldName(); if (stageName == "$out" && stage.firstElementType() == String) { NamespaceString outputNs(db, stage.firstElement().str()); uassert(17139, mongoutils::str::stream() << "Invalid $out target namespace, " << outputNs.ns(), outputNs.isValid()); ActionSet actions; actions.addAction(ActionType::remove); actions.addAction(ActionType::insert); if (shouldBypassDocumentValidationForCommand(cmdObj)) { actions.addAction(ActionType::bypassDocumentValidation); } Privilege::addPrivilegeToPrivilegeVector( &privileges, Privilege(ResourcePattern::forExactNamespace(outputNs), actions)); } else if (stageName == "$lookup" && stage.firstElementType() == Object) { NamespaceString fromNs(db, stage.firstElement()["from"].str()); Privilege::addPrivilegeToPrivilegeVector( &privileges, Privilege(ResourcePattern::forExactNamespace(fromNs), ActionType::find)); } else if (stageName == "$graphLookup" && stage.firstElementType() == Object) { NamespaceString fromNs(db, stage.firstElement()["from"].str()); Privilege::addPrivilegeToPrivilegeVector( &privileges, Privilege(ResourcePattern::forExactNamespace(fromNs), ActionType::find)); } }
Status checkAuthForWriteCommand(AuthorizationSession* authzSession, BatchedCommandRequest::BatchType cmdType, const OpMsgRequest& request) { std::vector<Privilege> privileges; ActionSet actionsOnCommandNSS; if (shouldBypassDocumentValidationForCommand(request.body)) { actionsOnCommandNSS.addAction(ActionType::bypassDocumentValidation); } NamespaceString cmdNSS; if (cmdType == BatchedCommandRequest::BatchType_Insert) { auto op = Insert::parse(IDLParserErrorContext("insert"), request); cmdNSS = op.getNamespace(); if (!op.getNamespace().isSystemDotIndexes()) { actionsOnCommandNSS.addAction(ActionType::insert); } else { // Special-case indexes until we have a command const auto swNssToIndex = getIndexedNss(op.getDocuments()); if (!swNssToIndex.isOK()) { return swNssToIndex.getStatus(); } const auto& nssToIndex = swNssToIndex.getValue(); privileges.push_back( Privilege(ResourcePattern::forExactNamespace(nssToIndex), ActionType::createIndex)); } } else if (cmdType == BatchedCommandRequest::BatchType_Update) { auto op = Update::parse(IDLParserErrorContext("update"), request); cmdNSS = op.getNamespace(); actionsOnCommandNSS.addAction(ActionType::update); // Upsert also requires insert privs if (containsUpserts(op.getUpdates())) { actionsOnCommandNSS.addAction(ActionType::insert); } } else { fassert(17251, cmdType == BatchedCommandRequest::BatchType_Delete); auto op = Delete::parse(IDLParserErrorContext("delete"), request); cmdNSS = op.getNamespace(); actionsOnCommandNSS.addAction(ActionType::remove); } if (!actionsOnCommandNSS.empty()) { privileges.emplace_back(ResourcePattern::forExactNamespace(cmdNSS), actionsOnCommandNSS); } if (authzSession->isAuthorizedForPrivileges(privileges)) return Status::OK(); return Status(ErrorCodes::Unauthorized, "unauthorized"); }
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(); }
void addPrivilegesRequiredForMapReduce(Command* commandTemplate, const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) { Config::OutputOptions outputOptions = Config::parseOutputOptions(dbname, cmdObj); ResourcePattern inputResource(commandTemplate->parseResourcePattern(dbname, cmdObj)); uassert(17142, mongolutils::str::stream() << "Invalid input resource " << inputResource.toString(), inputResource.isExactNamespacePattern()); out->push_back(Privilege(inputResource, ActionType::find)); if (outputOptions.outType != Config::INMEMORY) { ActionSet outputActions; outputActions.addAction(ActionType::insert); if (outputOptions.outType == Config::REPLACE) { outputActions.addAction(ActionType::remove); } else { outputActions.addAction(ActionType::update); } if (shouldBypassDocumentValidationForCommand(cmdObj)) { outputActions.addAction(ActionType::bypassDocumentValidation); } ResourcePattern outputResource( ResourcePattern::forExactNamespace(NamespaceString(outputOptions.finalNamespace))); uassert(17143, mongolutils::str::stream() << "Invalid target namespace " << outputResource.ns().ns(), outputResource.ns().isValid()); // TODO: check if outputNs exists and add createCollection privilege if not out->push_back(Privilege(outputResource, outputActions)); } }