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()); if (false && cmdObj["allowDiskUsage"].trueValue()) { // TODO no privilege for this yet. } 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); out->push_back(Privilege(ResourcePattern::forExactNamespace(outputNs), actions)); } }
void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) const override { ActionSet actions; actions.addAction(ActionType::convertToCapped); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); }
virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { ActionSet actions; actions.addAction(ActionType::insert); actions.addAction(ActionType::createIndex); if (shouldBypassDocumentValidationforCommand(cmdObj)) { actions.addAction(ActionType::bypassDocumentValidation); } if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(dbname), 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 (cmdObj.getFieldDotted("pipeline.0.$indexStats")) { Privilege::addPrivilegeToPrivilegeVector( &privileges, Privilege(ResourcePattern::forAnyNormalResource(), ActionType::indexStats)); } 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)); } }
Status checkAuthForWriteCommand(AuthorizationSession* authzSession, BatchedCommandRequest::BatchType cmdType, const NamespaceString& cmdNSS, const BSONObj& cmdObj) { vector<Privilege> privileges; ActionSet actionsOnCommandNSS; if (shouldBypassDocumentValidationForCommand(cmdObj)) { actionsOnCommandNSS.addAction(ActionType::bypassDocumentValidation); } if (cmdType == BatchedCommandRequest::BatchType_Insert) { if (!cmdNSS.isSystemDotIndexes()) { actionsOnCommandNSS.addAction(ActionType::insert); } else { // Special-case indexes until we have a command string nsToIndex, errMsg; if (!BatchedCommandRequest::getIndexedNS(cmdObj, &nsToIndex, &errMsg)) { return Status(ErrorCodes::FailedToParse, errMsg); } NamespaceString nssToIndex(nsToIndex); privileges.push_back( Privilege(ResourcePattern::forExactNamespace(nssToIndex), ActionType::createIndex)); } } else if (cmdType == BatchedCommandRequest::BatchType_Update) { actionsOnCommandNSS.addAction(ActionType::update); // Upsert also requires insert privs if (BatchedCommandRequest::containsUpserts(cmdObj)) { actionsOnCommandNSS.addAction(ActionType::insert); } } else { fassert(17251, cmdType == BatchedCommandRequest::BatchType_Delete); 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"); }
Privilege AuthorizationManager::_modifyPrivilegeForSpecialCases(const Privilege& privilege) { ActionSet newActions; newActions.addAllActionsFromSet(privilege.getActions()); std::string collectionName = NamespaceString(privilege.getResource()).coll; if (collectionName == "system.users") { newActions.removeAction(ActionType::find); newActions.removeAction(ActionType::insert); newActions.removeAction(ActionType::update); newActions.removeAction(ActionType::remove); newActions.addAction(ActionType::userAdmin); } else if (collectionName == "system.profle" && newActions.contains(ActionType::find)) { newActions.removeAction(ActionType::find); newActions.addAction(ActionType::profileRead); } return Privilege(privilege.getResource(), newActions); }
virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) { // TODO: update this with the new rules around user creation in 2.6. ActionSet actions; actions.addAction(ActionType::userAdmin); out->push_back(Privilege(dbname, actions)); }
Status checkAuthForRenameCollectionCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { NamespaceString sourceNS = NamespaceString(cmdObj.getStringField("renameCollection")); NamespaceString targetNS = NamespaceString(cmdObj.getStringField("to")); bool dropTarget = cmdObj["dropTarget"].trueValue(); if (sourceNS.db() == targetNS.db() && !sourceNS.isSystem() && !targetNS.isSystem()) { bool authed1 = client->getAuthorizationSession()->isAuthorizedForActionsOnResource( ResourcePattern::forDatabaseName(sourceNS.db()), ActionType::renameCollectionSameDB); bool authed2 = true; if (dropTarget) { authed2 = client->getAuthorizationSession()->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(targetNS), ActionType::dropCollection); } if (authed1 && authed2) { return Status::OK(); } } // Check privileges on source collection ActionSet actions; actions.addAction(ActionType::find); actions.addAction(ActionType::dropCollection); if (!client->getAuthorizationSession()->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(sourceNS), actions)) { return Status(ErrorCodes::Unauthorized, "Unauthorized"); } // Check privileges on dest collection actions.removeAllActions(); actions.addAction(ActionType::insert); actions.addAction(ActionType::createIndex); if (dropTarget) { actions.addAction(ActionType::dropCollection); } if (!client->getAuthorizationSession()->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(targetNS), actions)) { return Status(ErrorCodes::Unauthorized, "Unauthorized"); } return Status::OK(); }
void Pipeline::addRequiredPrivileges(const string& db, BSONObj cmdObj, vector<Privilege>* out) { ActionSet actions; actions.addAction(ActionType::find); out->push_back(Privilege(db + '.' + cmdObj.firstElement().str(), actions)); if (false && cmdObj["allowDiskUsage"].trueValue()) { // TODO no privilege for this yet. } BSONObj pipeline = cmdObj.getObjectField("pipeline"); BSONForEach(stageElem, pipeline) { BSONObj stage = stageElem.embeddedObjectUserCheck(); if (str::equals(stage.firstElementFieldName(), "$out")) { // TODO Figure out how to handle temp collection privileges. For now, using the // output ns is ok since we only do db-level privilege checks. const string outputNs = db + '.' + stage.firstElement().str(); ActionSet actions; // logically on output ns actions.addAction(ActionType::remove); actions.addAction(ActionType::insert); actions.addAction(ActionType::indexRead); // on temp ns due to implementation, but not logically on output ns actions.addAction(ActionType::createCollection); actions.addAction(ActionType::ensureIndex); actions.addAction(ActionType::dropCollection); actions.addAction(ActionType::renameCollectionSameDB); out->push_back(Privilege(outputNs, actions)); } }
virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) { ActionSet sourceActions; sourceActions.addAction(ActionType::find); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), sourceActions)); ActionSet targetActions; targetActions.addAction(ActionType::insert); targetActions.addAction(ActionType::createIndex); targetActions.addAction(ActionType::convertToCapped); std::string collection = cmdObj.getStringField("toCollection"); uassert(16708, "bad 'toCollection' value", !collection.empty()); out->push_back(Privilege(ResourcePattern::forExactNamespace( NamespaceString(dbname, collection)), targetActions)); }
virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { std::string ns = parseNs(dbname, cmdObj); ActionSet actions; actions.addAction(ActionType::insert); actions.addAction(ActionType::createIndex); // SERVER-11418 if (shouldBypassDocumentValidationForCommand(cmdObj)) { actions.addAction(ActionType::bypassDocumentValidation); } if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(NamespaceString(ns)), actions)) { return Status(ErrorCodes::Unauthorized, "Unauthorized"); } return Status::OK(); }
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()); if (false && cmdObj["allowDiskUsage"].trueValue()) { // TODO no privilege for this yet. } 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")) { // TODO Figure out how to handle temp collection privileges. For now, using the // output ns is ok since we only do db-level privilege checks. NamespaceString outputNs(db, stage.firstElement().str()); uassert(17139, mongoutils::str::stream() << "Invalid $out target namespace, " << outputNs.ns(), outputNs.isValid()); ActionSet actions; // logically on output ns actions.addAction(ActionType::remove); actions.addAction(ActionType::insert); // on temp ns due to implementation, but not logically on output ns actions.addAction(ActionType::createCollection); actions.addAction(ActionType::createIndex); actions.addAction(ActionType::dropCollection); actions.addAction(ActionType::renameCollectionSameDB); out->push_back(Privilege(ResourcePattern::forExactNamespace(outputNs), actions)); out->push_back(Privilege(ResourcePattern::forExactNamespace( NamespaceString(db, "system.indexes")), ActionType::find)); } }
virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { ActionSet actions; actions.addAction(ActionType::createIndex); Privilege p(parseResourcePattern(dbname, cmdObj), actions); if ( client->getAuthorizationSession()->isAuthorizedForPrivilege(p) ) return Status::OK(); return Status(ErrorCodes::Unauthorized, "Unauthorized"); }
// TODO: remove this default implementation so that all Command subclasses have to explicitly // declare their own. void Command::addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) { if (!requiresAuth()) { return; } ActionSet actions; actions.addAction(locktype() == WRITE ? ActionType::oldWrite : ActionType::oldRead); Privilege privilege(adminOnly() ? "admin" : dbname, actions); out->push_back(privilege); }
Privilege AuthorizationSession::_modifyPrivilegeForSpecialCases(const Privilege& privilege) { ActionSet newActions; newActions.addAllActionsFromSet(privilege.getActions()); NamespaceString ns( privilege.getResource() ); if (ns.coll() == "system.users") { newActions.removeAction(ActionType::find); newActions.removeAction(ActionType::insert); newActions.removeAction(ActionType::update); newActions.removeAction(ActionType::remove); newActions.addAction(ActionType::userAdmin); } else if (ns.coll() == "system.profile") { newActions.removeAction(ActionType::find); newActions.addAction(ActionType::profileRead); } else if (ns.coll() == "system.indexes" && newActions.contains(ActionType::find)) { newActions.removeAction(ActionType::find); newActions.addAction(ActionType::indexRead); } return Privilege(privilege.getResource(), newActions); }
virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) { ActionSet actions; actions.addAction(ActionType::logApplicationMessage); // TODO: Investigate if using the 'normal resource' // pattern of the new ResourcePattern API matches our // original use of SERVER_RESOURCE_NAME. We may want // to use somethine scoped to the given database name. out->push_back(Privilege(ResourcePattern::forAnyNormalResource(), actions)); }
Status checkAuthForMergeAuthzCollectionsCommand(Client* client, const BSONObj& cmdObj) { auth::MergeAuthzCollectionsArgs args; Status status = auth::parseMergeAuthzCollectionsCommand(cmdObj, &args); if (!status.isOK()) { return status; } AuthorizationSession* authzSession = AuthorizationSession::get(client); ActionSet actions; actions.addAction(ActionType::createUser); actions.addAction(ActionType::createRole); actions.addAction(ActionType::grantRole); actions.addAction(ActionType::revokeRole); if (args.drop) { actions.addAction(ActionType::dropUser); actions.addAction(ActionType::dropRole); } if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forAnyNormalResource(), actions)) { return Status(ErrorCodes::Unauthorized, "Not authorized to update user/role data using _mergeAuthzCollections" " command"); } if (!args.usersCollName.empty() && !authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(NamespaceString(args.usersCollName)), ActionType::find)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to read " << args.usersCollName); } if (!args.rolesCollName.empty() && !authzSession->isAuthorizedForActionsOnResource( ResourcePattern::forExactNamespace(NamespaceString(args.rolesCollName)), ActionType::find)) { return Status(ErrorCodes::Unauthorized, str::stream() << "Not authorized to read " << args.rolesCollName); } return Status::OK(); }
virtual void addRequiredPrivileges(const std::string& dbname, const BSONObj& cmdObj, std::vector<Privilege>* out) { ActionSet sourceActions; sourceActions.addAction(ActionType::find); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), sourceActions)); ActionSet targetActions; targetActions.addAction(ActionType::insert); targetActions.addAction(ActionType::createIndex); targetActions.addAction(ActionType::convertToCapped); const auto nssElt = cmdObj["toCollection"]; uassert(ErrorCodes::TypeMismatch, "'toCollection' must be of type String", nssElt.type() == BSONType::String); const NamespaceString nss(dbname, nssElt.valueStringData()); uassert(ErrorCodes::InvalidNamespace, str::stream() << "Invalid target namespace: " << nss.ns(), nss.isValid()); out->push_back(Privilege(ResourcePattern::forExactNamespace(nss), targetActions)); }
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, mongoutils::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, mongoutils::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)); } }
Status ActionSet::parseActionSetFromString(const std::string& actionsString, ActionSet* result) { std::vector<std::string> actionsList; splitStringDelim(actionsString, &actionsList, ','); ActionSet actions; for (size_t i = 0; i < actionsList.size(); i++) { ActionType action; Status status = ActionType::parseActionFromString(actionsList[i], &action); if (status != Status::OK()) { ActionSet empty; *result = empty; return status; } actions.addAction(action); } *result = actions; return Status::OK(); }
Status AuthorizationSession::checkAuthForUpdate(const NamespaceString& ns, const BSONObj& query, const BSONObj& update, bool upsert) { if (!upsert) { if (!isAuthorizedForActionsOnNamespace(ns, ActionType::update)) { return Status(ErrorCodes::Unauthorized, str::stream() << "not authorized for update on " << ns.ns()); } } else { ActionSet required; required.addAction(ActionType::update); required.addAction(ActionType::insert); if (!isAuthorizedForActionsOnNamespace(ns, required)) { return Status(ErrorCodes::Unauthorized, str::stream() << "not authorized for upsert on " << ns.ns()); } } return Status::OK(); }
Status ActionSet::parseActionSetFromStringVector(const std::vector<std::string>& actionsVector, ActionSet* result) { ActionSet actions; for (size_t i = 0; i < actionsVector.size(); i++) { ActionType action; Status status = ActionType::parseActionFromString(actionsVector[i], &action); if (status != Status::OK()) { ActionSet empty; *result = empty; return status; } if (action == ActionType::anyAction) { actions.addAllActions(); break; } actions.addAction(action); } *result = actions; return Status::OK(); }
Status AuthorizationManager::checkAuthForUpdate(const std::string& ns, bool upsert) { NamespaceString namespaceString(ns); if (!upsert) { if (!checkAuthorization(ns, ActionType::update)) { return Status(ErrorCodes::Unauthorized, mongoutils::str::stream() << "not authorized for update on " << ns, 0); } } else { ActionSet required; required.addAction(ActionType::update); required.addAction(ActionType::insert); if (!checkAuthorization(ns, required)) { return Status(ErrorCodes::Unauthorized, mongoutils::str::stream() << "not authorized for upsert on " << ns, 0); } } return Status::OK(); }
PrivilegeVector AuthorizationSession::getDefaultPrivileges() { PrivilegeVector defaultPrivileges; // If localhost exception is active (and no users exist), // return a vector of the minimum privileges required to bootstrap // a system and add the first user. if (_externalState->shouldAllowLocalhost()) { ResourcePattern adminDBResource = ResourcePattern::forDatabaseName(ADMIN_DBNAME); ActionSet setupAdminUserActionSet; setupAdminUserActionSet.addAction(ActionType::createUser); setupAdminUserActionSet.addAction(ActionType::grantRole); Privilege setupAdminUserPrivilege = Privilege(adminDBResource, setupAdminUserActionSet); ResourcePattern externalDBResource = ResourcePattern::forDatabaseName("$external"); Privilege setupExternalUserPrivilege = Privilege(externalDBResource, ActionType::createUser); ActionSet setupServerConfigActionSet; // If this server is an arbiter, add specific privileges meant to circumvent // the behavior of an arbiter in an authenticated replset. See SERVER-5479. if (_externalState->serverIsArbiter()) { setupServerConfigActionSet.addAction(ActionType::getCmdLineOpts); setupServerConfigActionSet.addAction(ActionType::getParameter); setupServerConfigActionSet.addAction(ActionType::serverStatus); setupServerConfigActionSet.addAction(ActionType::shutdown); } setupServerConfigActionSet.addAction(ActionType::addShard); setupServerConfigActionSet.addAction(ActionType::replSetConfigure); setupServerConfigActionSet.addAction(ActionType::replSetGetStatus); Privilege setupServerConfigPrivilege = Privilege(ResourcePattern::forClusterResource(), setupServerConfigActionSet); Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupAdminUserPrivilege); Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupExternalUserPrivilege); Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupServerConfigPrivilege); return defaultPrivileges; } return defaultPrivileges; }
void addPrivilegesRequiredForRenameCollection(const BSONObj& cmdObj, std::vector<Privilege>* out) { NamespaceString sourceNS = NamespaceString(cmdObj.getStringField("renameCollection")); NamespaceString targetNS = NamespaceString(cmdObj.getStringField("to")); uassert(17140, "Invalid source namespace " + sourceNS.ns(), sourceNS.isValid()); uassert(17141, "Invalid target namespace " + targetNS.ns(), targetNS.isValid()); ActionSet sourceActions; ActionSet targetActions; if (sourceNS.db() == targetNS.db()) { sourceActions.addAction(ActionType::renameCollectionSameDB); targetActions.addAction(ActionType::renameCollectionSameDB); } else { sourceActions.addAction(ActionType::cloneCollectionLocalSource); sourceActions.addAction(ActionType::dropCollection); targetActions.addAction(ActionType::createCollection); targetActions.addAction(ActionType::cloneCollectionTarget); targetActions.addAction(ActionType::createIndex); } out->push_back(Privilege(ResourcePattern::forExactNamespace(sourceNS), sourceActions)); out->push_back(Privilege(ResourcePattern::forExactNamespace(targetNS), targetActions)); }