bool run(OperationContext* opCtx,
             const string& dbname,
             const BSONObj& cmdObj,
             BSONObjBuilder& result) {
        auth::UsersInfoArgs args;
        Status status = auth::parseUsersInfoCommand(cmdObj, dbname, &args);
        uassertStatusOK(status);

        status = requireReadableAuthSchema26Upgrade(opCtx, getGlobalAuthorizationManager());
        uassertStatusOK(status);

        if ((args.target != auth::UsersInfoArgs::Target::kExplicitUsers || args.filter) &&
            (args.showPrivileges ||
             args.authenticationRestrictionsFormat == AuthenticationRestrictionsFormat::kShow)) {
            uasserted(ErrorCodes::IllegalOperation,
                      "Privilege or restriction details require exact-match usersInfo "
                      "queries.");
        }

        BSONArrayBuilder usersArrayBuilder;
        if (args.target == auth::UsersInfoArgs::Target::kExplicitUsers &&
            (args.showPrivileges ||
             args.authenticationRestrictionsFormat == AuthenticationRestrictionsFormat::kShow)) {
            // If you want privileges or restrictions you need to call getUserDescription on each
            // user.
            for (size_t i = 0; i < args.userNames.size(); ++i) {
                BSONObj userDetails;
                status = getGlobalAuthorizationManager()->getUserDescription(
                    opCtx, args.userNames[i], &userDetails);
                if (status.code() == ErrorCodes::UserNotFound) {
                    continue;
                }
                uassertStatusOK(status);

                // getUserDescription always includes credentials and restrictions, which may need
                // to be stripped out
                BSONObjBuilder strippedUser(usersArrayBuilder.subobjStart());
                for (const BSONElement& e : userDetails) {
                    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" &&
                        args.authenticationRestrictionsFormat ==
                            AuthenticationRestrictionsFormat::kOmit) {
                        continue;
                    }

                    strippedUser.append(e);
                }
                strippedUser.doneFast();
            }
        } else {
            // If you don't need privileges, or authenticationRestrictions, you can just do a
            // regular query on system.users
            std::vector<BSONObj> pipeline;
            if (args.target == auth::UsersInfoArgs::Target::kGlobal) {
                // Leave the pipeline unconstrained, we want to return every user.
            } else if (args.target == auth::UsersInfoArgs::Target::kDB) {
                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) {
                    usersMatchArray.append(BSON(AuthorizationManager::USER_NAME_FIELD_NAME
                                                << args.userNames[i].getUser()
                                                << AuthorizationManager::USER_DB_FIELD_NAME
                                                << args.userNames[i].getDB()));
                }
                pipeline.push_back(BSON("$match" << BSON("$or" << usersMatchArray.arr())));
            }
            // Order results by user field then db field, matching how UserNames are ordered
            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) {
                pipeline.push_back(BSON("$project" << BSON("credentials" << false)));
            }

            // Handle a user specified filter.
            if (args.filter) {
                pipeline.push_back(BSON("$match" << *args.filter));
            }

            DBDirectClient client(opCtx);

            BSONObjBuilder responseBuilder;
            AggregationRequest aggRequest(AuthorizationManager::usersCollectionNamespace,
                                          std::move(pipeline));
            uassertStatusOK(runAggregate(opCtx,
                                         AuthorizationManager::usersCollectionNamespace,
                                         aggRequest,
                                         aggRequest.serializeToCommandObj().toBson(),
                                         responseBuilder));
            CommandHelpers::appendSimpleCommandStatus(responseBuilder, true);
            auto response = CursorResponse::parseFromBSONThrowing(responseBuilder.obj());
            DBClientCursor cursor(&client,
                                  response.getNSS().toString(),
                                  response.getCursorId(),
                                  0,
                                  0,
                                  response.releaseBatch());

            while (cursor.more()) {
                usersArrayBuilder.append(cursor.next());
            }
        }
        result.append("users", usersArrayBuilder.arr());
        return true;
    }