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

        if (!args.hasPassword && !args.hasCustomData && !args.hasRoles &&
            !args.authenticationRestrictions && args.mechanisms.empty()) {
            uasserted(ErrorCodes::BadValue,
                      "Must specify at least one field to update in updateUser");
        }

        if (args.hasPassword && args.userName.getDB() == "$external") {
            uasserted(ErrorCodes::BadValue,
                      "Cannot set the password for users defined on the '$external' "
                      "database");
        }

        BSONObjBuilder queryBuilder;
        queryBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME, args.userName.getUser());
        queryBuilder.append(AuthorizationManager::USER_DB_FIELD_NAME, args.userName.getDB());

        BSONObjBuilder updateSetBuilder;
        BSONObjBuilder updateUnsetBuilder;
        if (args.hasPassword) {
            BSONObjBuilder credentialsBuilder(updateSetBuilder.subobjStart("credentials"));
            status = buildCredentials(&credentialsBuilder, args);
            uassertStatusOK(status);
            credentialsBuilder.done();
        } else if (!args.mechanisms.empty()) {
            status = trimCredentials(opCtx, &queryBuilder, &updateUnsetBuilder, args);
            uassertStatusOK(status);
        }

        if (args.hasCustomData) {
            updateSetBuilder.append("customData", args.customData);
        }

        if (args.authenticationRestrictions) {
            if (args.authenticationRestrictions->isEmpty()) {
                updateUnsetBuilder.append("authenticationRestrictions", "");
            } else {
                auto swParsedRestrictions =
                    parseAuthenticationRestriction(*args.authenticationRestrictions);
                uassertStatusOK(swParsedRestrictions.getStatus());

                updateSetBuilder.append("authenticationRestrictions",
                                        *args.authenticationRestrictions);
            }
        }

        if (args.hasRoles) {
            updateSetBuilder.append("roles", rolesVectorToBSONArray(args.roles));
        }

        BSONObj updateSet = updateSetBuilder.done();
        BSONObj updateUnset = updateUnsetBuilder.done();
        BSONObjBuilder updateDocumentBuilder;
        if (!updateSet.isEmpty()) {
            updateDocumentBuilder << "$set" << updateSet;
        }
        if (!updateUnset.isEmpty()) {
            updateDocumentBuilder << "$unset" << updateUnset;
        }

        ServiceContext* serviceContext = opCtx->getClient()->getServiceContext();
        AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);

        auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));

        // Role existence has to be checked after acquiring the update lock
        if (args.hasRoles) {
            for (size_t i = 0; i < args.roles.size(); ++i) {
                BSONObj ignored;
                status = authzManager->getRoleDescription(opCtx, args.roles[i], &ignored);
                uassertStatusOK(status);
            }
        }

        audit::logUpdateUser(Client::getCurrent(),
                             args.userName,
                             args.hasPassword,
                             args.hasCustomData ? &args.customData : NULL,
                             args.hasRoles ? &args.roles : NULL,
                             args.authenticationRestrictions);

        status = updatePrivilegeDocument(
            opCtx, args.userName, queryBuilder.done(), updateDocumentBuilder.done());
        // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
        authzManager->invalidateUserByName(opCtx, args.userName);
        uassertStatusOK(status);
        return true;
    }