Status parseAndValidateCreateRoleCommand(const BSONObj& cmdObj,
                                             const std::string& dbname,
                                             AuthorizationManager* authzManager,
                                             BSONObj* parsedRoleObj,
                                             BSONObj* parsedWriteConcern) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert("createRole");
        validFieldNames.insert("privileges");
        validFieldNames.insert("roles");
        validFieldNames.insert("writeConcern");

        Status status = _checkNoExtraFields(cmdObj, "createRole", validFieldNames);
        if (!status.isOK()) {
            return status;
        }

        status = _extractWriteConcern(cmdObj, parsedWriteConcern);
        if (!status.isOK()) {
            return status;
        }

        BSONObjBuilder roleObjBuilder;

        // Parse role name
        std::string roleName;
        status = bsonExtractStringField(cmdObj, "createRole", &roleName);
        if (!status.isOK()) {
            return status;
        }

        // Prevent creating roles in the local database
        if (dbname == "local") {
            return Status(ErrorCodes::BadValue, "Cannot create roles in the local database");
        }

        roleObjBuilder.append("_id", dbname + "." + roleName);
        roleObjBuilder.append(AuthorizationManager::ROLE_NAME_FIELD_NAME, roleName);
        roleObjBuilder.append(AuthorizationManager::ROLE_SOURCE_FIELD_NAME, dbname);

        // Parse privileges
        BSONElement privilegesElement;
        status = bsonExtractTypedField(cmdObj, "privileges", Array, &privilegesElement);
        if (!status.isOK()) {
            return status;
        }
        status = _parseAndValidatePrivilegeArray(BSONArray(privilegesElement.Obj()), NULL);
        if (!status.isOK()) {
            return status;
        }
        roleObjBuilder.append(privilegesElement);

        // Parse roles
        BSONElement rolesElement;
        status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
        if (!status.isOK()) {
            return status;
        }
        BSONArray modifiedRolesArray;
        status = _validateAndModifyRolesArray(rolesElement,
                                              dbname,
                                              authzManager,
                                              false,
                                              &modifiedRolesArray);
        if (!status.isOK()) {
            return status;
        }
        roleObjBuilder.append("roles", modifiedRolesArray);

        *parsedRoleObj = roleObjBuilder.obj();
        return Status::OK();
    }
    Status parseAndValidateUpdateUserCommand(const BSONObj& cmdObj,
                                             const std::string& dbname,
                                             AuthorizationManager* authzManager,
                                             BSONObj* parsedUpdateObj,
                                             UserName* parsedUserName,
                                             BSONObj* parsedWriteConcern) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert("updateUser");
        validFieldNames.insert("customData");
        validFieldNames.insert("pwd");
        validFieldNames.insert("roles");
        validFieldNames.insert("writeConcern");

        Status status = _checkNoExtraFields(cmdObj, "updateUser", validFieldNames);
        if (!status.isOK()) {
            return status;
        }

        status = _extractWriteConcern(cmdObj, parsedWriteConcern);
        if (!status.isOK()) {
            return status;
        }

        BSONObjBuilder updateSetBuilder;

        // Parse user name
        std::string userName;
        status = bsonExtractStringField(cmdObj, "updateUser", &userName);
        if (!status.isOK()) {
            return status;
        }
        *parsedUserName = UserName(userName, dbname);

        // Parse password
        if (cmdObj.hasField("pwd")) {
            std::string clearTextPassword;
            status = bsonExtractStringField(cmdObj, "pwd", &clearTextPassword);
            if (!status.isOK()) {
                return status;
            }

            std::string password = auth::createPasswordDigest(userName, clearTextPassword);
            updateSetBuilder.append("credentials.MONGODB-CR", password);
        }


        // Parse custom data
        if (cmdObj.hasField("customData")) {
            BSONElement element;
            status = bsonExtractTypedField(cmdObj, "customData", Object, &element);
            if (!status.isOK()) {
                return status;
            }
            updateSetBuilder.append("customData", element.Obj());
        }

        // Parse roles
        if (cmdObj.hasField("roles")) {
            BSONElement rolesElement;
            Status status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
            if (!status.isOK()) {
                return status;
            }

            BSONArray modifiedRolesObj;
            status = _validateAndModifyRolesArray(rolesElement,
                                                  dbname,
                                                  authzManager,
                                                  true,
                                                  &modifiedRolesObj);
            if (!status.isOK()) {
                return status;
            }

            updateSetBuilder.append("roles", modifiedRolesObj);
        }

        BSONObj updateSet = updateSetBuilder.obj();
        if (updateSet.isEmpty()) {
            return Status(ErrorCodes::UserModificationFailed,
                          "Must specify at least one field to update in updateUser");
        }

        *parsedUpdateObj = BSON("$set" << updateSet);
        return Status::OK();
    }
    Status parseAndValidateUpdateUserCommand(const BSONObj& cmdObj,
                                             const std::string& dbname,
                                             AuthorizationManager* authzManager,
                                             BSONObj* parsedUpdateObj,
                                             UserName* parsedUserName) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert("updateUser");
        validFieldNames.insert("customData");
        validFieldNames.insert("pwd");
        validFieldNames.insert("roles");
        validFieldNames.insert("writeConcern");

        // Iterate through all fields in command object and make sure there are no unexpected
        // ones.
        for (BSONObjIterator iter(cmdObj); iter.more(); iter.next()) {
            StringData fieldName = (*iter).fieldNameStringData();
            if (!validFieldNames.count(fieldName.toString())) {
                return Status(ErrorCodes::BadValue,
                              mongoutils::str::stream() << "\"" << fieldName << "\" is not "
                                      "a valid argument to createUser");
            }
        }

        BSONObjBuilder updateSetBuilder;

        // Parse user name
        std::string userName;
        Status status = bsonExtractStringField(cmdObj, "updateUser", &userName);
        if (!status.isOK()) {
            return status;
        }
        *parsedUserName = UserName(userName, dbname);

        // Parse password
        if (cmdObj.hasField("pwd")) {
            std::string clearTextPassword;
            status = bsonExtractStringField(cmdObj, "pwd", &clearTextPassword);
            if (!status.isOK()) {
                return status;
            }

            std::string password = auth::createPasswordDigest(userName, clearTextPassword);
            updateSetBuilder.append("credentials.MONGODB-CR", password);
        }


        // Parse custom data
        if (cmdObj.hasField("customData")) {
            BSONElement element;
            status = bsonExtractTypedField(cmdObj, "customData", Object, &element);
            if (!status.isOK()) {
                return status;
            }
            updateSetBuilder.append("customData", element.Obj());
        }

        // Parse roles
        if (cmdObj.hasField("roles")) {
            BSONElement rolesElement;
            Status status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
            if (!status.isOK()) {
                return status;
            }

            BSONArray modifiedRolesObj;
            status = _validateAndModifyRolesArray(rolesElement,
                                                  dbname,
                                                  authzManager,
                                                  &modifiedRolesObj);
            if (!status.isOK()) {
                return status;
            }

            updateSetBuilder.append("roles", modifiedRolesObj);
        }

        BSONObj updateSet = updateSetBuilder.obj();
        if (updateSet.isEmpty()) {
            return Status(ErrorCodes::UserModificationFailed,
                          "Must specify at least one field to update in updateUser");
        }

        *parsedUpdateObj = BSON("$set" << updateSet);
        return Status::OK();
    }
    Status parseAndValidateCreateUserCommand(const BSONObj& cmdObj,
                                             const std::string& dbname,
                                             AuthorizationManager* authzManager,
                                             BSONObj* parsedUserObj,
                                             BSONObj* parsedWriteConcern) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert("createUser");
        validFieldNames.insert("customData");
        validFieldNames.insert("pwd");
        validFieldNames.insert("roles");
        validFieldNames.insert("writeConcern");

        Status status = _checkNoExtraFields(cmdObj, "createUser", validFieldNames);
        if (!status.isOK()) {
            return status;
        }

        status = _extractWriteConcern(cmdObj, parsedWriteConcern);
        if (!status.isOK()) {
            return status;
        }

        BSONObjBuilder userObjBuilder;

        // Parse user name
        std::string userName;
        status = bsonExtractStringField(cmdObj, "createUser", &userName);
        if (!status.isOK()) {
            return status;
        }

        // Prevent creating users in the local database
        if (dbname == "local") {
            return Status(ErrorCodes::BadValue, "Cannot create users in the local database");
        }

        userObjBuilder.append("_id", dbname + "." + userName);
        userObjBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME, userName);
        userObjBuilder.append(AuthorizationManager::USER_SOURCE_FIELD_NAME, dbname);


        // Parse password
        if (cmdObj.hasField("pwd")) {
            std::string clearTextPassword;
            status = bsonExtractStringField(cmdObj, "pwd", &clearTextPassword);
            if (!status.isOK()) {
                return status;
            }

            std::string password = auth::createPasswordDigest(userName, clearTextPassword);
            userObjBuilder.append("credentials", BSON("MONGODB-CR" << password));
        } else {
            if (dbname != "$external") {
                return Status(ErrorCodes::BadValue,
                              "Must provide a 'pwd' field for all user documents, except those"
                                      " with '$external' as the user's source");
            }
        }


        // Parse custom data
        if (cmdObj.hasField("customData")) {
            BSONElement element;
            status = bsonExtractTypedField(cmdObj, "customData", Object, &element);
            if (!status.isOK()) {
                return status;
            }
            userObjBuilder.append("customData", element.Obj());
        }

        // Parse roles
        if (cmdObj.hasField("roles")) {
            BSONElement rolesElement;
            status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
            if (!status.isOK()) {
                return status;
            }
            BSONArray modifiedRolesArray;
            status = _validateAndModifyRolesArray(rolesElement,
                                                  dbname,
                                                  authzManager,
                                                  true,
                                                  &modifiedRolesArray);
            if (!status.isOK()) {
                return status;
            }

            userObjBuilder.append("roles", modifiedRolesArray);
        }

        *parsedUserObj = userObjBuilder.obj();

        // Make sure document to insert is valid
        V2UserDocumentParser parser;
        status = parser.checkValidUserDocument(*parsedUserObj);
        if (!status.isOK()) {
            return status;
        }

        return Status::OK();
    }
    Status parseAndValidateCreateUserCommand(const BSONObj& cmdObj,
                                             const std::string& dbname,
                                             AuthorizationManager* authzManager,
                                             BSONObj* parsedUserObj) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert("createUser");
        validFieldNames.insert("customData");
        validFieldNames.insert("pwd");
        validFieldNames.insert("roles");
        validFieldNames.insert("writeConcern");

        // Iterate through all fields in command object and make sure there are no unexpected
        // ones.
        for (BSONObjIterator iter(cmdObj); iter.more(); iter.next()) {
            StringData fieldName = (*iter).fieldNameStringData();
            if (!validFieldNames.count(fieldName.toString())) {
                return Status(ErrorCodes::BadValue,
                              mongoutils::str::stream() << "\"" << fieldName << "\" is not "
                                      "a valid argument to createUser");
            }
        }

        BSONObjBuilder userObjBuilder;
        userObjBuilder.append("_id", OID::gen());

        // Parse user name
        std::string userName;
        Status status = bsonExtractStringField(cmdObj, "createUser", &userName);
        if (!status.isOK()) {
            return status;
        }

        // Prevent creating users in the local database
        if (dbname == "local") {
            return Status(ErrorCodes::BadValue, "Cannot create users in the local database");
        }

        userObjBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME, userName);
        userObjBuilder.append(AuthorizationManager::USER_SOURCE_FIELD_NAME, dbname);


        // Parse password
        if (cmdObj.hasField("pwd")) {
            std::string clearTextPassword;
            status = bsonExtractStringField(cmdObj, "pwd", &clearTextPassword);
            if (!status.isOK()) {
                return status;
            }

            std::string password = auth::createPasswordDigest(userName, clearTextPassword);
            userObjBuilder.append("credentials", BSON("MONGODB-CR" << password));
        } else {
            if (dbname != "$external") {
                return Status(ErrorCodes::BadValue,
                              "Must provide a 'pwd' field for all user documents, except those"
                                      " with '$external' as the user's source");
            }
        }


        // Parse custom data
        if (cmdObj.hasField("customData")) {
            BSONElement element;
            status = bsonExtractTypedField(cmdObj, "customData", Object, &element);
            if (!status.isOK()) {
                return status;
            }
            userObjBuilder.append("customData", element.Obj());
        }

        // Parse roles
        if (cmdObj.hasField("roles")) {
            BSONElement rolesElement;
            status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
            if (!status.isOK()) {
                return status;
            }
            BSONArray modifiedRolesArray;
            status = _validateAndModifyRolesArray(rolesElement,
                                                  dbname,
                                                  authzManager,
                                                  &modifiedRolesArray);
            if (!status.isOK()) {
                return status;
            }

            userObjBuilder.append("roles", modifiedRolesArray);
        }

        *parsedUserObj = userObjBuilder.obj();

        // Make sure document to insert is valid
        V2PrivilegeDocumentParser parser;
        status = parser.checkValidPrivilegeDocument(*parsedUserObj);
        if (!status.isOK()) {
            return status;
        }

        return Status::OK();
    }