Now that stripInaccessible()
and WITH_SECURITY_ENFORCED
are GA I wonder what is the most readable and efficient way to do a
SELECT all_accessible_fields_for_user FROM AnyObject WHERE Id = ...
The main problem at hand is determining which fields to include in a query.
Since this is, by necessity, a runtime calculation, we'll still need to use dynamic soql (i.e. Database.query(querystring)
)
The WITH_SECURITY_ENFORCED
keyword for SOQL appears to throw an exception if an inaccessible field is requested. Exceptions as flow control are a bad idea, and it doesn't tell us which fields were the problem. Doesn't seem that this would be helpful for the problem at hand.
Security.stripInaccessible()
does allow us to figure out which fields aren't accessible, and doesn't throw an exception, but it requires something (results of a query, maybe an in-memory instance would also work) to presumably iterate through and test the object/field access.
I think going through the field describe info is still probably going to be the winner in terms of efficiency and length.
Something like...
List<String> accessibleFieldApiNames = new List<String>();
for(Schema.SObjectField field :Schema.SObjectType.Account.fields.getMap().values()){
Schema.DescribeFieldResult dfr = field.getDescribe();
if(dfr.isAccessible()){
accessibleFieldApiNames.add(dfr.getName());
}
}
String dynamicQuery = 'SELECT {0} FROM {1} LIMIT 100';
List<Account> accounts = Database.query(String.format(dynamicQuery, new List<String>{
String.join(accessibleFieldApiNames, ', '),
'Account'
}));
I think that's going to be hard to beat. Using Security.stripInaccessible()
requires that you are querying all fields to begin with, so I don't see a way around using the DescribeFieldResult
bit at the beginning.
with sharing
modifier in the class this is used in, wouldn't it?
This is the best I came up with myself:
public SObject queryAllAccessible(Id recordId) {
SObjectType type = recordId.getSObjectType();
List<String> allFields = new List<String>(type.getDescribe().fields.getMap().keySet());
String soql = String.format('SELECT {0} FROM {1} WHERE Id = :recordId',
new List<String>{ String.join(allFields, ','), ''+type });
return Security.stripInaccessible(AccessType.READABLE, Database.query(soql))
.getRecords()[0];
}