bool run(OperationContext* txn,
             const string& dbname,
             BSONObj& cmdObj,
             int options,
             string& errmsg,
             BSONObjBuilder& result) {
        auth::CreateOrUpdateUserArgs args;
        Status status = auth::parseCreateOrUpdateUserCommands(cmdObj, "updateUser", dbname, &args);
        if (!status.isOK()) {
            return appendCommandStatus(result, status);
        }

        if (!args.hasHashedPassword && !args.hasCustomData && !args.hasRoles) {
            return appendCommandStatus(
                result,
                Status(ErrorCodes::BadValue,
                       "Must specify at least one field to update in updateUser"));
        }

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

        BSONObjBuilder updateSetBuilder;
        if (args.hasHashedPassword) {
            BSONObjBuilder credentialsBuilder(updateSetBuilder.subobjStart("credentials"));

            AuthorizationManager* authzManager = getGlobalAuthorizationManager();
            int authzVersion;
            Status status = authzManager->getAuthorizationVersion(txn, &authzVersion);
            if (!status.isOK()) {
                return appendCommandStatus(result, status);
            }

            // Add SCRAM credentials for appropriate authSchemaVersions
            if (authzVersion > AuthorizationManager::schemaVersion26Final) {
                BSONObj scramCred = scram::generateCredentials(
                    args.hashedPassword, saslGlobalParams.scramIterationCount);
                credentialsBuilder.append("SCRAM-SHA-1", scramCred);
            } else {  // Otherwise default to MONGODB-CR
                credentialsBuilder.append("MONGODB-CR", args.hashedPassword);
            }
            credentialsBuilder.done();
        }
        if (args.hasCustomData) {
            updateSetBuilder.append("customData", args.customData);
        }
        if (args.hasRoles) {
            updateSetBuilder.append("roles", rolesVectorToBSONArray(args.roles));
        }

        ServiceContext* serviceContext = txn->getClient()->getServiceContext();
        stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));

        AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
        status = requireAuthSchemaVersion26Final(txn, authzManager);
        if (!status.isOK()) {
            return appendCommandStatus(result, status);
        }


        // 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(
                    txn, args.roles[i], PrivilegeFormat::kOmit, &ignored);
                if (!status.isOK()) {
                    return appendCommandStatus(result, status);
                }
            }
        }

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

        status =
            updatePrivilegeDocument(txn, args.userName, BSON("$set" << updateSetBuilder.done()));
        // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
        authzManager->invalidateUserByName(args.userName);
        return appendCommandStatus(result, status);
    }