diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2018-01-24 14:50:01 -0500 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2018-03-26 19:33:36 -0400 |
commit | 20f97bc8f600025e9d8085c29d7e41fca342a5a9 (patch) | |
tree | 308dc811598d3f482aca94543f60f1ab8e1aba79 /src/mongo/db | |
parent | e93e3e7930f844bf830715dfecbb602eefe48b8e (diff) | |
download | mongo-20f97bc8f600025e9d8085c29d7e41fca342a5a9.tar.gz |
SERVER-32975: Expose mechanism info in usersInfo
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/auth/user_management_commands_parser.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/auth/user_management_commands_parser.h | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands.cpp | 78 |
3 files changed, 70 insertions, 19 deletions
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp index bacb4f9636b..141b74ca07d 100644 --- a/src/mongo/db/auth/user_management_commands_parser.cpp +++ b/src/mongo/db/auth/user_management_commands_parser.cpp @@ -334,6 +334,7 @@ Status parseUsersInfoCommand(const BSONObj& cmdObj, StringData dbname, UsersInfo validFieldNames.insert("showAuthenticationRestrictions"); validFieldNames.insert("showPrivileges"); validFieldNames.insert("showCredentials"); + validFieldNames.insert("filter"); Status status = _checkNoExtraFields(cmdObj, "usersInfo", validFieldNames); if (!status.isOK()) { @@ -387,6 +388,15 @@ Status parseUsersInfoCommand(const BSONObj& cmdObj, StringData dbname, UsersInfo : AuthenticationRestrictionsFormat::kOmit; } + + const auto filterObj = cmdObj["filter"]; + if (!filterObj.eoo()) { + if (filterObj.type() != Object) { + return Status(ErrorCodes::TypeMismatch, "filter must be an Object"); + } + parsedArgs->filter = filterObj.Obj(); + } + return Status::OK(); } diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h index 1d1012d7d6e..535c7257f9b 100644 --- a/src/mongo/db/auth/user_management_commands_parser.h +++ b/src/mongo/db/auth/user_management_commands_parser.h @@ -109,6 +109,7 @@ struct UsersInfoArgs { AuthenticationRestrictionsFormat authenticationRestrictionsFormat = AuthenticationRestrictionsFormat::kOmit; bool showCredentials = false; + boost::optional<BSONObj> filter; }; /** diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index 990283dffc2..e256579ce7e 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -59,10 +59,12 @@ #include "mongo/db/auth/user_management_commands_parser.h" #include "mongo/db/client.h" #include "mongo/db/commands.h" +#include "mongo/db/commands/run_aggregate.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/jsobj.h" #include "mongo/db/operation_context.h" #include "mongo/db/ops/write_ops.h" +#include "mongo/db/query/cursor_response.h" #include "mongo/db/service_context.h" #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/s/write_ops/batched_command_response.h" @@ -1333,13 +1335,13 @@ public: return CommandHelpers::appendCommandStatus(result, status); } - if (args.allForDB && + if ((args.allForDB || args.filter) && (args.showPrivileges || args.authenticationRestrictionsFormat == AuthenticationRestrictionsFormat::kShow)) { return CommandHelpers::appendCommandStatus( result, Status(ErrorCodes::IllegalOperation, - "Can only get privilege or restriction details on exact-match usersInfo " + "Privilege or restriction details require exact-match usersInfo " "queries.")); } @@ -1363,8 +1365,17 @@ public: // to be stripped out BSONObjBuilder strippedUser(usersArrayBuilder.subobjStart()); for (const BSONElement& e : userDetails) { - if (!args.showCredentials && e.fieldNameStringData() == "credentials") { - continue; + if (e.fieldNameStringData() == "credentials") { + BSONArrayBuilder mechanismNamesBuilder; + BSONObj mechanismsObj = e.Obj(); + for (const BSONElement& mechanismElement : mechanismsObj) { + mechanismNamesBuilder.append(mechanismElement.fieldNameStringData()); + } + strippedUser.append("mechanisms", mechanismNamesBuilder.arr()); + + if (!args.showCredentials) { + continue; + } } if (e.fieldNameStringData() == "authenticationRestrictions" && @@ -1380,10 +1391,10 @@ public: } else { // If you don't need privileges, or authenticationRestrictions, you can just do a // regular query on system.users - BSONObjBuilder queryBuilder; + std::vector<BSONObj> pipeline; if (args.allForDB) { - queryBuilder.append("query", - BSON(AuthorizationManager::USER_DB_FIELD_NAME << dbname)); + pipeline.push_back( + BSON("$match" << BSON(AuthorizationManager::USER_DB_FIELD_NAME << dbname))); } else { BSONArrayBuilder usersMatchArray; for (size_t i = 0; i < args.userNames.size(); ++i) { @@ -1392,25 +1403,54 @@ public: << AuthorizationManager::USER_DB_FIELD_NAME << args.userNames[i].getDB())); } - queryBuilder.append("query", BSON("$or" << usersMatchArray.arr())); + pipeline.push_back(BSON("$match" << BSON("$or" << usersMatchArray.arr()))); } // Order results by user field then db field, matching how UserNames are ordered - queryBuilder.append("orderby", BSON("user" << 1 << "db" << 1)); - - BSONObjBuilder projection; - projection.append("authenticationRestrictions", 0); + pipeline.push_back(BSON("$sort" << BSON("user" << 1 << "db" << 1))); + + // Authentication restrictions are only rendered in the single user case. + pipeline.push_back(BSON("$project" << BSON("authenticationRestrictions" << false))); + + // Rewrite the credentials object into an array of its fieldnames. + pipeline.push_back( + BSON("$addFields" << BSON("mechanisms" + << BSON("$map" << BSON("input" << BSON("$objectToArray" + << "$credentials") + << "as" + << "cred" + << "in" + << "$$cred.k"))))); + + // Remove credentials, they're not required in the output if (!args.showCredentials) { - projection.append("credentials", 0); + pipeline.push_back(BSON("$project" << BSON("credentials" << false))); } - Status status = - queryAuthzDocument(opCtx, - AuthorizationManager::usersCollectionNamespace, - queryBuilder.done(), - projection.done(), - [&](const BSONObj& obj) { usersArrayBuilder.append(obj); }); + + // Handle a user specified filter. + if (args.filter) { + pipeline.push_back(BSON("$match" << *args.filter)); + } + + BSONObjBuilder responseBuilder; + AggregationRequest aggRequest(AuthorizationManager::usersCollectionNamespace, + std::move(pipeline)); + Status status = runAggregate(opCtx, + AuthorizationManager::usersCollectionNamespace, + aggRequest, + aggRequest.serializeToCommandObj().toBson(), + responseBuilder); if (!status.isOK()) { return CommandHelpers::appendCommandStatus(result, status); } + + CommandHelpers::appendCommandStatus(responseBuilder, Status::OK()); + auto swResponse = CursorResponse::parseFromBSON(responseBuilder.obj()); + if (!swResponse.isOK()) { + return CommandHelpers::appendCommandStatus(result, swResponse.getStatus()); + } + for (const BSONObj& obj : swResponse.getValue().getBatch()) { + usersArrayBuilder.append(obj); + } } result.append("users", usersArrayBuilder.arr()); return true; |