From ee7ea7ea7f8eb423cd2ee8ec928f2b50e014ac52 Mon Sep 17 00:00:00 2001 From: Spencer T Brody Date: Sun, 6 Oct 2013 14:49:49 -0400 Subject: [PATCH] SERVER-6246 SERVER-9515 Update usersInfo and rolesInfo commands to new API --- jstests/auth/basic_role_auth.js | 2 +- src/mongo/db/auth/authorization_manager.cpp | 3 +- src/mongo/db/auth/authorization_manager.h | 1 + .../db/auth/authz_manager_external_state.h | 1 + .../auth/authz_manager_external_state_d.cpp | 4 +- .../db/auth/authz_manager_external_state_d.h | 1 + .../authz_manager_external_state_mock.cpp | 1 + .../auth/authz_manager_external_state_mock.h | 1 + .../auth/authz_manager_external_state_s.cpp | 21 +- .../db/auth/authz_manager_external_state_s.h | 1 + .../auth/user_management_commands_parser.cpp | 209 ++++++++++++------ .../db/auth/user_management_commands_parser.h | 31 ++- .../db/commands/user_management_commands.cpp | 138 +++++++----- src/mongo/shell/db.js | 2 +- 14 files changed, 275 insertions(+), 141 deletions(-) diff --git a/jstests/auth/basic_role_auth.js b/jstests/auth/basic_role_auth.js index 79751443ea5..6f7189185ba 100644 --- a/jstests/auth/basic_role_auth.js +++ b/jstests/auth/basic_role_auth.js @@ -186,7 +186,7 @@ var testOps = function(db, allowedActions) { }, db); checkErr(allowedActions.hasOwnProperty('user_r'), function() { - var result = db.runCommand({usersInfo: /.*/}); + var result = db.runCommand({usersInfo: 1}); if (!result.ok) { throw new Error(tojson(result)); } diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 6a0fce1b4eb..412bfb1e73f 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -225,8 +225,9 @@ namespace mongo { Status AuthorizationManager::queryAuthzDocument( const NamespaceString& collectionName, const BSONObj& query, + const BSONObj& projection, const boost::function& resultProcessor) { - return _externalState->query(collectionName, query, resultProcessor); + return _externalState->query(collectionName, query, projection, resultProcessor); } Status AuthorizationManager::updateAuthzDocuments(const NamespaceString& collectionName, diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 29647ebe4b0..5a5e9fd8271 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -217,6 +217,7 @@ namespace mongo { */ Status queryAuthzDocument(const NamespaceString& collectionName, const BSONObj& query, + const BSONObj& projection, const boost::function& resultProcessor); // Checks to see if "doc" is a valid privilege document, assuming it is stored in the diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index b81561d5f29..a722ba00bfe 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -156,6 +156,7 @@ namespace mongo { */ virtual Status query(const NamespaceString& collectionName, const BSONObj& query, + const BSONObj& projection, const boost::function& resultProcessor) = 0; /** diff --git a/src/mongo/db/auth/authz_manager_external_state_d.cpp b/src/mongo/db/auth/authz_manager_external_state_d.cpp index 0e251cb5dd8..5411eacecdb 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp @@ -237,11 +237,12 @@ namespace { Status AuthzManagerExternalStateMongod::query( const NamespaceString& collectionName, const BSONObj& query, + const BSONObj& projection, const boost::function& resultProcessor) { try { DBDirectClient client; Client::GodScope gs; - client.query(resultProcessor, collectionName.ns(), query); + client.query(resultProcessor, collectionName.ns(), query, &projection); return Status::OK(); } catch (const DBException& e) { return e.toStatus(); @@ -452,6 +453,7 @@ namespace { Status status = query( AuthorizationManager::rolesCollectionNamespace, BSONObj(), + BSONObj(), boost::bind(addRoleFromDocumentOrWarn, &newRoleGraph, _1)); if (!status.isOK()) return status; diff --git a/src/mongo/db/auth/authz_manager_external_state_d.h b/src/mongo/db/auth/authz_manager_external_state_d.h index eb29200d5e2..ad136d75f47 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.h +++ b/src/mongo/db/auth/authz_manager_external_state_d.h @@ -65,6 +65,7 @@ namespace mongo { BSONObj* result); virtual Status query(const NamespaceString& collectionName, const BSONObj& query, + const BSONObj& projection, const boost::function& resultProcessor); virtual Status insert(const NamespaceString& collectionName, const BSONObj& document, diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.cpp b/src/mongo/db/auth/authz_manager_external_state_mock.cpp index 4a7b8168c84..9a4feed74be 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp @@ -213,6 +213,7 @@ namespace { Status AuthzManagerExternalStateMock::query( const NamespaceString& collectionName, const BSONObj& query, + const BSONObj&, const boost::function& resultProcessor) { std::vector iterVector; Status status = _queryVector(collectionName, query, &iterVector); diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.h b/src/mongo/db/auth/authz_manager_external_state_mock.h index 639716606af..8d323ca8d00 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.h +++ b/src/mongo/db/auth/authz_manager_external_state_mock.h @@ -87,6 +87,7 @@ namespace mongo { virtual Status query(const NamespaceString& collectionName, const BSONObj& query, + const BSONObj& projection, // Currently unused in mock const boost::function& resultProcessor); // This implementation does not understand uniqueness constraints. diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp index 8720e0aaf55..7288f823f9a 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -79,8 +79,14 @@ namespace { AuthorizationManager::usersCollectionNamespace)); BSONObj cmdResult; conn->get()->runCommand( - userName.getDB().toString(), // TODO: Change usersInfo so this command can always go to "admin". - BSON("usersInfo" << userName.getUser() << "details" << true), + "admin", + BSON("usersInfo" << + BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME << + userName.getUser() << + AuthorizationManager::USER_SOURCE_FIELD_NAME << + userName.getDB())) << + "showPrivileges" << true << + "showCredentials" << true), cmdResult); if (!cmdResult["ok"].trueValue()) { int code = cmdResult["code"].numberInt(); @@ -102,8 +108,12 @@ namespace { AuthorizationManager::rolesCollectionNamespace)); BSONObj cmdResult; conn->get()->runCommand( - roleName.getDB().toString(), // TODO: Change rolesInfo so this command can always go to "admin". - BSON("rolesInfo" << roleName.getRole()), + "admin", + BSON("rolesInfo" << + BSON_ARRAY(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << + roleName.getRole() << + AuthorizationManager::ROLE_SOURCE_FIELD_NAME << + roleName.getDB()))), cmdResult); if (!cmdResult["ok"].trueValue()) { int code = cmdResult["code"].numberInt(); @@ -138,10 +148,11 @@ namespace { Status AuthzManagerExternalStateMongos::query( const NamespaceString& collectionName, const BSONObj& query, + const BSONObj& projection, const boost::function& resultProcessor) { try { scoped_ptr conn(getConnectionForAuthzCollection(collectionName)); - conn->get()->query(resultProcessor, collectionName.ns(), query); + conn->get()->query(resultProcessor, collectionName.ns(), query, &projection); return Status::OK(); } catch (const DBException& e) { return e.toStatus(); diff --git a/src/mongo/db/auth/authz_manager_external_state_s.h b/src/mongo/db/auth/authz_manager_external_state_s.h index 340a81f195d..14e5b44c50e 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.h +++ b/src/mongo/db/auth/authz_manager_external_state_s.h @@ -66,6 +66,7 @@ namespace mongo { BSONObj* result); virtual Status query(const NamespaceString& collectionName, const BSONObj& query, + const BSONObj& projection, const boost::function& resultProcessor); virtual Status insert(const NamespaceString& collectionName, const BSONObj& document, diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp index b6f8149a4b0..19bb5b4f4f2 100644 --- a/src/mongo/db/auth/user_management_commands_parser.cpp +++ b/src/mongo/db/auth/user_management_commands_parser.cpp @@ -81,44 +81,82 @@ namespace auth { return Status::OK(); } - Status parseRoleNamesFromBSONArray(const BSONArray& rolesArray, - const StringData& dbname, - const StringData& rolesFieldName, - std::vector* parsedRoleNames) { - for (BSONObjIterator it(rolesArray); it.more(); it.next()) { - BSONElement element = *it; - if (element.type() == String) { - parsedRoleNames->push_back(RoleName(element.String(), dbname)); - } - else if (element.type() == Object) { - BSONObj roleObj = element.Obj(); + // Extracts a UserName or RoleName object from a BSONElement. + template + Status _parseNameFromBSONElement(const BSONElement& element, + const StringData& dbname, + const StringData& nameFieldName, + const StringData& sourceFieldName, + Name* parsedName) { + if (element.type() == String) { + *parsedName = Name(element.String(), dbname); + } + else if (element.type() == Object) { + BSONObj obj = element.Obj(); - std::string roleNameString; - std::string roleSource; - Status status = bsonExtractStringField(roleObj, - AuthorizationManager::ROLE_NAME_FIELD_NAME, - &roleNameString); - if (!status.isOK()) { - return status; - } - status = bsonExtractStringField(roleObj, - AuthorizationManager::ROLE_SOURCE_FIELD_NAME, - &roleSource); - if (!status.isOK()) { - return status; - } + std::string name; + std::string source; + Status status = bsonExtractStringField(obj, nameFieldName, &name); + if (!status.isOK()) { + return status; + } + status = bsonExtractStringField(obj, sourceFieldName, &source); + if (!status.isOK()) { + return status; + } - parsedRoleNames->push_back(RoleName(roleNameString, roleSource)); - } - else { - return Status(ErrorCodes::BadValue, - mongoutils::str::stream() << "Values in \"" << rolesFieldName << - "\" array must be sub-documents or strings"); - } + *parsedName = Name(name, source); + } + else { + return Status(ErrorCodes::BadValue, + "User and role names must be either strings or objects"); } return Status::OK(); } + // Extracts UserName or RoleName objects from a BSONArray of role/user names. + template + Status _parseNamesFromBSONArray(const BSONArray& array, + const StringData& dbname, + const StringData& nameFieldName, + const StringData& sourceFieldName, + std::vector* parsedNames) { + for (BSONObjIterator it(array); it.more(); it.next()) { + BSONElement element = *it; + Name name; + Status status = _parseNameFromBSONElement(element, + dbname, + nameFieldName, + sourceFieldName, + &name); + if (!status.isOK()) { + return status; + } + parsedNames->push_back(name); + } + return Status::OK(); + } + + Status _parseUserNamesFromBSONArray(const BSONArray& usersArray, + const StringData& dbname, + std::vector* parsedUserNames) { + return _parseNamesFromBSONArray(usersArray, + dbname, + AuthorizationManager::USER_NAME_FIELD_NAME, + AuthorizationManager::USER_SOURCE_FIELD_NAME, + parsedUserNames); + } + + Status parseRoleNamesFromBSONArray(const BSONArray& rolesArray, + const StringData& dbname, + std::vector* parsedRoleNames) { + return _parseNamesFromBSONArray(rolesArray, + dbname, + AuthorizationManager::ROLE_NAME_FIELD_NAME, + AuthorizationManager::ROLE_SOURCE_FIELD_NAME, + parsedRoleNames); + } + Status _extractRoleDataFromBSONArray(const BSONElement& rolesElement, const std::string& dbname, std::vector *parsedRoleData) { @@ -208,7 +246,6 @@ namespace auth { status = parseRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()), dbname, - rolesFieldName, parsedRoleNames); if (!status.isOK()) { return status; @@ -342,44 +379,89 @@ namespace auth { return Status::OK(); } - Status parseAndValidateInfoCommands(const BSONObj& cmdObj, - const StringData& cmdName, - const std::string& dbname, - bool* parsedAnyDB, - BSONElement* parsedNameFilter) { + Status parseUsersInfoCommand(const BSONObj& cmdObj, + const StringData& dbname, + UsersInfoArgs* parsedArgs) { unordered_set validFieldNames; - validFieldNames.insert(cmdName.toString()); - validFieldNames.insert("anyDB"); - validFieldNames.insert("writeConcern"); - validFieldNames.insert("details"); + validFieldNames.insert("usersInfo"); + validFieldNames.insert("showPrivileges"); + validFieldNames.insert("showCredentials"); - Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames); + Status status = _checkNoExtraFields(cmdObj, "usersInfo", validFieldNames); if (!status.isOK()) { return status; } - if (cmdObj[cmdName].type() != String && cmdObj[cmdName].type() != RegEx) { - return Status(ErrorCodes::BadValue, - mongoutils::str::stream() << "Argument to \"" << cmdName << - "\"command must be either a string or a regex"); - } - *parsedNameFilter = cmdObj[cmdName]; - - - bool anyDB = false; - if (cmdObj.hasField("anyDB")) { - if (dbname == "admin") { - Status status = bsonExtractBooleanField(cmdObj, "anyDB", &anyDB); - if (!status.isOK()) { - return status; - } - } else { - return Status(ErrorCodes::BadValue, - mongoutils::str::stream() << "\"anyDB\" argument to \"" << cmdName << - "\"command is only valid when run on the \"admin\" database"); + if (cmdObj["usersInfo"].numberInt() == 1) { + parsedArgs->allForDB = true; + } else if (cmdObj["usersInfo"].type() == Array) { + status = _parseUserNamesFromBSONArray(BSONArray(cmdObj["usersInfo"].Obj()), + dbname, + &parsedArgs->userNames); + if (!status.isOK()) { + return status; } + } else { + UserName name; + status = _parseNameFromBSONElement(cmdObj["usersInfo"], + dbname, + AuthorizationManager::USER_NAME_FIELD_NAME, + AuthorizationManager::USER_SOURCE_FIELD_NAME, + &name); + if (!status.isOK()) { + return status; + } + parsedArgs->userNames.push_back(name); + } + + status = bsonExtractBooleanFieldWithDefault(cmdObj, + "showPrivileges", + false, + &parsedArgs->showPrivileges); + if (!status.isOK()) { + return status; + } + status = bsonExtractBooleanFieldWithDefault(cmdObj, + "showCredentials", + false, + &parsedArgs->showCredentials); + if (!status.isOK()) { + return status; + } + + return Status::OK(); + } + + Status parseRolesInfoCommand(const BSONObj& cmdObj, + const StringData& dbname, + std::vector* parsedRoleNames) { + unordered_set validFieldNames; + validFieldNames.insert("rolesInfo"); + + Status status = _checkNoExtraFields(cmdObj, "rolesInfo", validFieldNames); + if (!status.isOK()) { + return status; + } + + if (cmdObj["rolesInfo"].type() == Array) { + status = parseRoleNamesFromBSONArray(BSONArray(cmdObj["rolesInfo"].Obj()), + dbname, + parsedRoleNames); + if (!status.isOK()) { + return status; + } + } else { + RoleName name; + status = _parseNameFromBSONElement(cmdObj["rolesInfo"], + dbname, + AuthorizationManager::ROLE_NAME_FIELD_NAME, + AuthorizationManager::ROLE_SOURCE_FIELD_NAME, + &name); + if (!status.isOK()) { + return status; + } + parsedRoleNames->push_back(name); } - *parsedAnyDB = anyDB; return Status::OK(); } @@ -467,7 +549,6 @@ namespace auth { } status = parseRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()), dbname, - "roles", &parsedArgs->roles); if (!status.isOK()) { return status; diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h index 98ce7984dc3..58c0a427828 100644 --- a/src/mongo/db/auth/user_management_commands_parser.h +++ b/src/mongo/db/auth/user_management_commands_parser.h @@ -99,17 +99,29 @@ namespace auth { const std::string& dbname, BSONObj* parsedWriteConcern); + struct UsersInfoArgs { + std::vector userNames; + bool allForDB; + bool showPrivileges; + bool showCredentials; + UsersInfoArgs() : allForDB(false), showPrivileges(false), showCredentials(false) {} + }; + /** - * Takes a command object describing an invocation of the "usersInfo" or "rolesInfo" commands - * (which command it is is specified in the "cmdName" argument) and parses out a BSONElement - * with the user/role name filter to be applied, as well as the anyDB boolean. - * Also validates the input and returns a non-ok Status if there is anything wrong. + * Takes a command object describing an invocation of the "usersInfo" command and parses out + * all the arguments into the "parsedArgs" output param. */ - Status parseAndValidateInfoCommands(const BSONObj& cmdObj, - const StringData& cmdName, - const std::string& dbname, - bool* parsedAnyDb, - BSONElement* parsedNameFilter); + Status parseUsersInfoCommand(const BSONObj& cmdObj, + const StringData& dbname, + UsersInfoArgs* parsedArgs); + + /** + * Takes a command object describing an invocation of the "rolesInfo" command and parses out + * the role names requested into the "parsedRoleNames" output param. + */ + Status parseRolesInfoCommand(const BSONObj& cmdObj, + const StringData& dbname, + std::vector* parsedRoleNames); struct CreateOrUpdateRoleArgs { RoleName roleName; @@ -173,7 +185,6 @@ namespace auth { */ Status parseRoleNamesFromBSONArray(const BSONArray& rolesArray, const StringData& dbname, - const StringData& rolesFieldName, std::vector* parsedRoleNames); } // namespace auth diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index bd8c47c593b..70b070d725d 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -980,56 +980,79 @@ namespace mongo { BSONObjBuilder& result, bool fromRepl) { - bool anyDB = false; - BSONElement usersFilter; - Status status = auth::parseAndValidateInfoCommands(cmdObj, - "usersInfo", - dbname, - &anyDB, - &usersFilter); + auth::UsersInfoArgs args; + Status status = auth::parseUsersInfoCommand(cmdObj, dbname, &args); if (!status.isOK()) { addStatus(status, result); return false; } - bool wantsDetails = cmdObj["details"].trueValue(); - if (wantsDetails) { - if (anyDB || usersFilter.type() != String) { - addStatus(Status(ErrorCodes::IllegalOperation, - "Cannot only get privilege details on exact-match usersInfo " - "queries."), - result); - return false; - } - - BSONObj userDetails; - status = getGlobalAuthorizationManager()->getUserDescription( - UserName(usersFilter.str(), dbname), &userDetails); - if (!status.isOK()) { - addStatus(status, result); - return false; - } - result.append("users", BSON_ARRAY(userDetails)); - return true; - } - - - BSONObjBuilder queryBuilder; - queryBuilder.appendAs(usersFilter, AuthorizationManager::USER_NAME_FIELD_NAME); - if (!anyDB) { - queryBuilder.append(AuthorizationManager::USER_SOURCE_FIELD_NAME, dbname); + if (args.allForDB && args.showPrivileges) { + addStatus(Status(ErrorCodes::IllegalOperation, + "Cannot only get privilege details on exact-match usersInfo " + "queries."), + result); + return false; } BSONArrayBuilder usersArrayBuilder; - BSONArrayBuilder& (BSONArrayBuilder::* appendBSONObj) (const BSONObj&) = - &BSONArrayBuilder::append; - const boost::function function = - boost::bind(appendBSONObj, &usersArrayBuilder, _1); - AuthorizationManager* authzManager = getGlobalAuthorizationManager(); - authzManager->queryAuthzDocument(NamespaceString("admin.system.users"), - queryBuilder.done(), - function); + if (args.showPrivileges) { + // If you want privileges you need to call getUserDescription on each user. + for (size_t i = 0; i < args.userNames.size(); ++i) { + BSONObj userDetails; + status = getGlobalAuthorizationManager()->getUserDescription( + args.userNames[i], &userDetails); + if (status.code() == ErrorCodes::UserNotFound) { + continue; + } + if (!status.isOK()) { + addStatus(status, result); + return false; + } + if (!args.showCredentials) { + // getUserDescription always includes credentials, need to strip it out + BSONObjBuilder userWithoutCredentials(usersArrayBuilder.subobjStart()); + for (BSONObjIterator it(userDetails); it.more(); ) { + BSONElement e = it.next(); + if (e.fieldNameStringData() != "credentials") + userWithoutCredentials.append(e); + } + userWithoutCredentials.doneFast(); + } else { + usersArrayBuilder.append(userDetails); + } + } + } else { + // If you don't need privileges, you can just do a regular query on system.users + BSONObjBuilder queryBuilder; + if (args.allForDB) { + queryBuilder.append(AuthorizationManager::USER_SOURCE_FIELD_NAME, dbname); + } else { + BSONArrayBuilder usersMatchArray; + for (size_t i = 0; i < args.userNames.size(); ++i) { + usersMatchArray.append(BSON(AuthorizationManager::USER_NAME_FIELD_NAME << + args.userNames[i].getUser() << + AuthorizationManager::USER_SOURCE_FIELD_NAME << + args.userNames[i].getDB())); + } + queryBuilder.append("$or", usersMatchArray.arr()); + } + + AuthorizationManager* authzManager = getGlobalAuthorizationManager(); + BSONObjBuilder projection; + if (!args.showCredentials) { + projection.append("credentials", 0); + } + BSONArrayBuilder& (BSONArrayBuilder::* appendBSONObj) (const BSONObj&) = + &BSONArrayBuilder::append; + const boost::function function = + boost::bind(appendBSONObj, &usersArrayBuilder, _1); + authzManager->queryAuthzDocument(AuthorizationManager::usersCollectionNamespace, + queryBuilder.done(), + projection.done(), + function); + } result.append("users", usersArrayBuilder.arr()); return true; } @@ -1577,7 +1600,6 @@ namespace mongo { std::vector roles; status = auth::parseRoleNamesFromBSONArray(BSONArray(roleDoc["roles"].Obj()), roleName.getDB(), - "roles", &roles); for (vector::iterator it = rolesToAdd.begin(); it != rolesToAdd.end(); ++it) { @@ -1600,7 +1622,6 @@ namespace mongo { status = auth::parseRoleNamesFromBSONArray( BSONArray(roleDoc["indirectRoles"].Obj()), roleName.getDB(), - "indirectRoles", &indirectSubordinatesOfToAdd); if (!status.isOK()) { addStatus(status, result); @@ -1712,7 +1733,6 @@ namespace mongo { std::vector roles; status = auth::parseRoleNamesFromBSONArray(BSONArray(roleDoc["roles"].Obj()), roleName.getDB(), - "roles", &roles); if (!status.isOK()) { addStatus(status, result); @@ -1940,26 +1960,28 @@ namespace mongo { BSONObjBuilder& result, bool fromRepl) { - bool anyDB = false; - BSONElement rolesFilter; - Status status = auth::parseAndValidateInfoCommands(cmdObj, - "rolesInfo", - dbname, - &anyDB, - &rolesFilter); + std::vector roleNames; + Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &roleNames); if (!status.isOK()) { addStatus(status, result); return false; } - BSONObj roleObj; - status = getGlobalAuthorizationManager()->getRoleDescription( - RoleName(rolesFilter.str(), dbname), &roleObj); - if (!status.isOK()) { - addStatus(status, result); - return false; + BSONArrayBuilder rolesArrayBuilder; + for (size_t i = 0; i < roleNames.size(); ++i) { + BSONObj roleDetails; + status = getGlobalAuthorizationManager()->getRoleDescription( + roleNames[i], &roleDetails); + if (status.code() == ErrorCodes::RoleNotFound) { + continue; + } + if (!status.isOK()) { + addStatus(status, result); + return false; + } + rolesArrayBuilder.append(roleDetails); } - result.append("roles", BSON_ARRAY(roleObj)); + result.append("roles", rolesArrayBuilder.arr()); return true; } diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js index d1efd0dae6d..db4327ca0bd 100644 --- a/src/mongo/shell/db.js +++ b/src/mongo/shell/db.js @@ -1234,7 +1234,7 @@ DB.prototype.getUser = function(username) { } DB.prototype.getUsers = function() { - var res = this.runCommand({usersInfo: /.*/}); + var res = this.runCommand({usersInfo: 1}); if (!res.ok) { throw Error(res.errmsg); }