bool run(OperationContext* opCtx,
             const string& dbname,
             const BSONObj& cmdObj,
             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 &&
            !args.authenticationRestrictions) {
            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;
        BSONObjBuilder updateUnsetBuilder;
        if (args.hasHashedPassword) {
            BSONObjBuilder credentialsBuilder(updateSetBuilder.subobjStart("credentials"));

            AuthorizationManager* authzManager = getGlobalAuthorizationManager();
            int authzVersion;
            Status status = authzManager->getAuthorizationVersion(opCtx, &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.load());
                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.authenticationRestrictions) {
            if (args.authenticationRestrictions->isEmpty()) {
                updateUnsetBuilder.append("authenticationRestrictions", "");
            } else {
                auto swParsedRestrictions =
                    parseAuthenticationRestriction(*args.authenticationRestrictions);
                if (!swParsedRestrictions.isOK()) {
                    return appendCommandStatus(result, 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();
        stdx::lock_guard<stdx::mutex> lk(getAuthzDataMutex(serviceContext));

        AuthorizationManager* authzManager = AuthorizationManager::get(serviceContext);
        status = requireAuthSchemaVersion26Final(opCtx, 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(opCtx, args.roles[i], &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,
                             args.authenticationRestrictions);

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