If you need to remove users or groups in bulk from FileNet objects you can use the following custom sweep action script.

importPackage(java.lang);
importClass(java.lang.System);
importPackage(Packages.com.filenet.api.sweep);
importPackage(Packages.com.filenet.api.property);
importPackage(Packages.com.filenet.api.security);
importPackage(Packages.com.filenet.api.core);
importPackage(Packages.com.filenet.api.constants);
importPackage(Packages.com.filenet.api.engine);

function onPolicySweep(SweepObject, SweepPolicy, SweepItems){
}

function onSweep(sweepObject, sweepItems){

	// Liste der zu entfernenden Benutzer/Gruppen (Gross-/Kleinschreibung wird ignoriert)
	var removeFromAcl = [
		'editors_group@p8.tta',
		'readers_group@p8.tta'
	];

	var hcc = HandlerCallContext.getInstance();
	hcc.traceDetail("Entering RemoveAclHandler.onSweep");
	hcc.traceDetail("sweepObject = " + sweepObject.getProperties().getIdValue(PropertyNames.ID) + " sweepItems.length = " + sweepItems.length);

	ii = 0;
	for (ii = 0; ii < sweepItems.length; ii++){

		if (hcc != null && hcc.isShuttingDown()){
			throw new EngineRuntimeException(
				ExceptionCode.E_BACKGROUND_TASK_TERMINATED,
				this.constructor.name + " is terminating prematurely because the server is shutting down"
			);
		}

		var item = sweepItems[ii].getTarget();
		var msg = "sweepItems[" + ii + "]= " + item.getProperties().getIdValue("ID");
		hcc.traceDetail(msg);

		try{
			var CEObject = castToCorrectType(item, hcc);

			if (CEObject == null) {
				hcc.traceDetail("   WARNUNG: Unbekannter Objekttyp, wird übersprungen");
				sweepItems[ii].setOutcome(SweepItemOutcome.SKIPPED, "Unbekannter Objekttyp");
				continue;
			}

			var removed = removeSecurityEntries(CEObject, removeFromAcl, hcc);

			if (removed > 0) {
				sweepItems[ii].setOutcome(SweepItemOutcome.PROCESSED, removed + " Einträge entfernt");
			} else {
				sweepItems[ii].setOutcome(SweepItemOutcome.PROCESSED, "Keine der gesuchten Einträge gefunden");
			}

		}catch (ioe){
			sweepItems[ii].setOutcome(SweepItemOutcome.FAILED, "RemoveAclHandler: " + ioe.rhinoException.getMessage());
			hcc.traceDetail("FAILED " + ioe.rhinoException.getMessage());
		}
	}

	hcc.traceDetail("Exiting RemoveAclHandler.onSweep");
}


/*
 * Erforderliche Properties für den Sweep
 */
function getRequiredProperties()
{
	var pnames = ['Id', 'Permissions'];
	return pnames.toString();
}


/*
 * Entfernt die angegebenen Benutzer/Gruppen aus der bestehenden ACL.
 * Gibt die Anzahl der entfernten Einträge zurück.
 */
function removeSecurityEntries(ceObject, removeFromAcl, hcc) {

	var acl = ceObject.get_Permissions(); // AccessPermissionList
	var removedCount = 0;

	if(hcc) hcc.traceDetail("  ACL-Einträge vor dem Entfernen: " + acl.size());

	// Zu entfernende Namen in Kleinbuchstaben für Vergleich
	var removeListLower = [];
	for (var r = 0; r < removeFromAcl.length; r++) {
		removeListLower.push(removeFromAcl[r].toLowerCase());
	}

	// Zu entfernende Einträge sammeln (nicht während Iteration entfernen!)
	var toRemove = [];
	var iterator = acl.iterator();
	while (iterator.hasNext()) {
		var permission = iterator.next();
		var granteeName = permission.get_GranteeName();

		if (granteeName != null && removeListLower.indexOf(granteeName.toLowerCase()) !== -1) {
			if(hcc) hcc.traceDetail("  Markiere zum Entfernen: " + granteeName);
			toRemove.push(permission);
		} else {
			if(hcc) hcc.traceDetail("  Behalte Eintrag: " + granteeName);
		}
	}

	// Gesammelte Einträge jetzt entfernen
	for (var i = 0; i < toRemove.length; i++) {
		acl.remove(toRemove[i]);
		removedCount++;
		if(hcc) hcc.traceDetail("  Entfernt: " + toRemove[i].get_GranteeName());
	}

	if(hcc) hcc.traceDetail("  ACL-Einträge nach dem Entfernen: " + acl.size());
	if(hcc) hcc.traceDetail("  Insgesamt entfernt: " + removedCount);

	// Nur speichern wenn tatsächlich etwas geändert wurde
	if (removedCount > 0) {
		ceObject.set_Permissions(acl);
		ceObject.save(com.filenet.api.constants.RefreshMode.NO_REFRESH);
	}

	return removedCount;
}


/*
 * Erkennt den Objekttyp anhand von instanceof und castet entsprechend.
 * Unterstützt: Document, Folder, CustomObject
 */
function castToCorrectType(item, hcc) {
	try {
		var className = item.getClassName();
		if(hcc) hcc.traceDetail("   Objektklasse: " + className);

		if (item instanceof com.filenet.api.core.Document) {
			if(hcc) hcc.traceDetail("   Typ erkannt: Document");
			return com.filenet.api.core.Document(item);

		} else if (item instanceof com.filenet.api.core.Folder) {
			if(hcc) hcc.traceDetail("   Typ erkannt: Folder");
			return com.filenet.api.core.Folder(item);

		} else if (item instanceof com.filenet.api.core.CustomObject) {
			if(hcc) hcc.traceDetail("   Typ erkannt: CustomObject");
			return com.filenet.api.core.CustomObject(item);

		} else {
			if(hcc) hcc.traceDetail("   instanceof-Prüfung fehlgeschlagen, versuche Fallback...");
			return castByClassName(item, className, hcc);
		}

	} catch(e) {
		if(hcc) hcc.traceDetail("   Fehler beim Typ-Casting: " + e.message);
		return null;
	}
}


/*
 * Fallback: Casting über die Klassenhierarchie (für Subklassen).
 */
function castByClassName(item, className, hcc) {
	try {
		var classDesc = item.get_ClassDescription();

		while (classDesc != null) {
			var superClassName = classDesc.get_SymbolicName();
			if(hcc) hcc.traceDetail("   Prüfe Basisklasse: " + superClassName);

			if (superClassName === "Document") {
				if(hcc) hcc.traceDetail("   Fallback Typ erkannt: Document (Subklasse von " + className + ")");
				return com.filenet.api.core.Document(item);

			} else if (superClassName === "Folder") {
				if(hcc) hcc.traceDetail("   Fallback Typ erkannt: Folder (Subklasse von " + className + ")");
				return com.filenet.api.core.Folder(item);

			} else if (superClassName === "CustomObject") {
				if(hcc) hcc.traceDetail("   Fallback Typ erkannt: CustomObject (Subklasse von " + className + ")");
				return com.filenet.api.core.CustomObject(item);

			} else if (superClassName === "IndependentlyPersistableObject" || superClassName === "RepositoryObject") {
				break;
			}

			classDesc = classDesc.get_SuperclassDescription();
		}

	} catch(e) {
		if(hcc) hcc.traceDetail("   Fehler beim Fallback-Casting: " + e.message);
	}

	return null;
}
```

**Kernpunkt: Warum zwei Schritte beim Entfernen?**

Das Entfernen direkt während der Iteration würde eine `ConcurrentModificationException` werfen, da man eine Liste nicht verändern darf während man sie durchläuft. Daher wird in zwei Schritten vorgegangen — erst sammeln, dann entfernen:
```
Schritt 1: Iteration → zu entfernende Einträge in "toRemove" sammeln
Schritt 2: Über "toRemove" iterieren → Einträge aus der ACL entfernen