It seems that the Apex row-level security enforcement by with sharing
keyword is not applied to the field OwnerId
, when it is directly passed to update statement.
public with sharing class MyRecordUpdate {
// ...
public void changeOwnerToMe(Id recordId) {
MyRecord__c rec = new MyRecord__c(Id=recordId, OwnerId=UserInfo.getUserId());
update rec;
}
}
In the above code, supposing that the MyRecord__c
object is private in Organization-Wide sharing defaults, and each records are shared by manually. Its Object-level security = CRUD enabled, and Field-level security = Not set.
If the record specified by recordId
is owned by other user and granted read-only access to the current user, the modification of OwnerId
should raise an insufficient privilege error (it actually behaves that from standard UI or REST API). But when executing MyRecordUpdate#changeOwnerId()
in the context of user with standard profile (not admin user), it silently succeeds the owner modification.
This only happens when the update statement argument record is created by new
, not applied when queried from SOQL. Also it will not happen to other fields, like Name
.
public with sharing class MyRecordUpdate {
// ...
// supposing that context user is granted read-only access to `recordId` record
public void changeOwnerToMeUsingQuery(Id recordId) {
MyRecord__c rec = [SELECT Id FROM MyRecord__c WHERE Id = :recordId];
rec.OwnerId = UserInfo.getUserId();
update rec; // throws INSUFFICIENT_ACCESS_OR_READONLY error
}
public void updateName(Id recordId, String name) {
MyRecord__c rec = new MyRecord__c(Id=recordId, Name=name);
update rec; // throws INSUFFICIENT_ACCESS_OR_READONLY error
}
}
Is there anyone who encountered to this situation? I've tested the above in my scratch org.
EDIT: Added the code to demonstrate this issue. https://github.com/stomita/apex-rls-ownerid-issue
changeOwnerToMe
&changeOwnerToMeUsingQuery
methods were able to update the OwnerId field (code run under Standard User context, following the instructions you have mentioned). But I did get insufficient access exceptions for the Name and Text01__c fields, which was expected. While the OwnerId update can be restricted with some additional apex code, its weird that the field is getting updated by an user having read only access to the record.The following field types don’t return a FieldPermissions record because they are assumed to always be readable and writable. OwnerId
in the SOAP API developer guide [developer.salesforce.com/docs/atlas.en-us.api.meta/api/…. While its not directly related to the problem here, it sounds like OwnerId field is handled quite differently by Salesforce when compared to the other field.changeOwnerToMeUsingQuery
doesn't throw insufficient privilege error (unexpected), but the other environment it throws (expected). I cannot figure the difference of them.