function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Mikola SenykMikola Senyk 

Apex class "with sharing" keyword can update read only records

Working with inline Visualforce page we have faced with security issue. Apex class "with sharing" keyword updates Opportunity with Read-Only access.
We have OWD for Opportunity Public Read/Write. User profile has Read Only access to Opportunity. In standard Salesforce UI user can't edit Opportunity.
It is correct because we have object level security (OLS) RW and record level security (RLS) Read-Only and more strict (OLS) wins.

To reproduce issue we have created inline Visualforce page and controller extension for Opportunity object.
Controller extension:
public with sharing class oppExt{
    
	public Opportunity opp;
	
	public oppExt(ApexPages.StandardController stdController){
        opp = (Opportunity)stdController.getRecord();
        status = 'start';
        can_upd = Schema.sObjectType.Opportunity.fields.Name.isUpdateable();
        ura = [SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess, HasAllAccess FROM UserRecordAccess WHERE RecordId = :opp.Id AND UserId = :UserInfo.getUserId() LIMIT 1];
    }
    
    public String status {get;set;}
    public Boolean can_upd {get;set;}
    public UserRecordAccess ura {get;set;}
	
    public void updOpportunity(){
        opp.Name += '0';
        try{
            update opp;
            status = 'success';
        } catch(Exception e){
            status = 'failed | ' + e.getMessage();
        }
    }   

}
Visualforce page:
<apex:page standardController="Opportunity" extensions="oppExt">
    <apex:form >
		<apex:commandButton action="{!updOpportunity}" value="update opportunity"/>
		<br/>name updateable: {!can_upd}
		<br/>result: {!status}
		<br/>opp name: {!Opportunity.Name}
		<br/>vf UsrRecAcc (r/e/d/all): {!Opportunity.userRecordAccess.HasReadAccess}/{!Opportunity.userRecordAccess.HasEditAccess}/{!Opportunity.userRecordAccess.HasDeleteAccess}/{!Opportunity.userRecordAccess.HasAllAccess}
		<br/>apex UsrRecAcc (r/e/d/all): {!ura.HasReadAccess}/{!ura.HasEditAccess}/{!ura.HasDeleteAccess}/{!ura.HasAllAccess}
    </apex:form>
</apex:page>

As you see it is simple command button to update Opportunity. And it works, but should not.

It is interesting direct query to UserRecordAccess object return false for HasEditAccess:
SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess, HasAllAccess FROM UserRecordAccess WHERE RecordId = :opp.Id AND UserId = :UserInfo.getUserId() LIMIT 1
However query the same field with Opportunity return true:
{!Opportunity.userRecordAccess.HasEditAccess}
James LoghryJames Loghry
First, I would update the updOpportunity method to return a PageReference.  That way you can add any exceptions as Page Messages and rerender / display the error or exception messages on the VF page should an exception be thrown.  If the opportunity save is succesul, then handle that condition appropriate as well.  As it stands you're simply setting the status to failed if an exception is thrown and that's it.

It's possible that you're swallowing the exception and not realizing it.

Also, are you viewing the inline VF as System Administrator or another profile with ModifyAllData privileges?  If so, then you'll be able to edit the record regardless of org wide defaults / sharing settings.
Mikola SenykMikola Senyk
I've tested this page from standard user that has only read access for opportunity. This user has no ModifyAllData permission.
Standard UI works properly - user can't edit opportunity.

I don't think it is related of swallowing exception, because opportunity has been successfully updated.
It looks like internal Salesforce bug related to Apex class keyword "with sharing".