You need to sign in to do that
Don't have an account?
Mikola 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:
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}
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}
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.
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".