Edit: The app requires Manage permission on the Web for this to work
Actually I found that it is possible.
You have to break the permissions on the list first and then change the role assignments on the list for whichever users you want:
userList is a list of login names, and roleType is an SP.RoleType (https://msdn.microsoft.com/en-us/library/office/jj246683.aspx)
setUserListPermissionsBatch: function (listName, userList, roleType) {
"use strict";
Permissions.resetStored();
var context,
list,
roleDefBindingColl,
i,
oUser,
deferred;
deferred = new $.Deferred(function () {
if (userList && listName && roleType) {
// Create the role
context = SP.ClientContext.get_current();
list = context.get_web().get_lists().getByTitle(listName);
roleDefBindingColl = SP.RoleDefinitionBindingCollection.newObject(context);
roleDefBindingColl.add(context.get_web().get_roleDefinitions().getByType(roleType));
// Loop the users
for (i = 0; i < userList.length; i++) {
oUser = context.get_web().get_siteUsers().getByLoginName(userList[i]);
list.get_roleAssignments().add(oUser, roleDefBindingColl);
}
context.load(list);
context.executeQueryAsync(
function () {
return deferred.resolve();
},
function (sender, args) {
console.log(args.get_message());
return deferred.reject(args);
}
);
}
});
return deferred.promise();
},
To remove the permissions, I had to copy the current user permissions, remove the one I wanted to get rid of so you keep other permissions for that user on the same list.
Then just re-add the new roles:
/ Removes permissions for a user
removeUserListPermissions: function (listName, userLogin, roleType) {
"use strict";
var context,
list,
user,
userRoles,
x,
rolDefs,
newRoleCollection,
deferred;
deferred = new $.Deferred(function () {
if (userLogin && listName) {
context = SP.ClientContext.get_current();
list = context.get_web().get_lists().getByTitle(listName);
user = context.get_web().get_siteUsers().getByLoginName(userLogin);
userRoles = list.get_roleAssignments().getByPrincipal(user);
context.load(user);
context.load(list);
context.load(userRoles, 'RoleDefinitionBindings');
context.executeQueryAsync(Function.createDelegate(this, function () {
newRoleCollection = SP.RoleDefinitionBindingCollection.newObject(context);
for (x = 0; x < userRoles.get_roleDefinitionBindings().get_count() ; x++) {
rolDefs = userRoles.get_roleDefinitionBindings().itemAt(x);
if (rolDefs.get_roleTypeKind() !== roleType) {
newRoleCollection.add(rolDefs);
}
}
userRoles.deleteObject();
list.get_roleAssignments().add(user, newRoleCollection);
context.load(list);
context.executeQueryAsync(function () {
return deferred.resolve();
}, function (sender, args) {
console.log("Error deleted permissions: " + args.get_message());
return deferred.reject();
});
}),
function (sender, args) {
console.log(args.get_message());
return deferred.reject();
});
} else {
return deferred.resolve();
}
});
return deferred.promise();
}
To do groups I'm just using :
// Loop the groups
for (i = 0; i < groupList.length; i++) {
list.get_roleAssignments().add(context.get_web().get_siteGroups().getByName(groupList[i]), roleDefBindingColl);
}
instead of looping the users