While developing secure controller actions for a Lightning Component, I ran across an issue checking accessibility for an Account
level attribute related a Contact
.
For example:
Schema.SObjectType.Contact.fields.getMap().get('Account.My_custom_field__c').getDescribe().isAccessible()
The piece of code, get('Account.My_custom_field__c')
returns null, and the code throws an exception. I understand why it is happening — the field it's trying to check is technically on the Account
object, not the Contact
object.
The way I have written my code is, I have an array of fields, which I later use to piece together a query. I loop over each field, check field accessibility, then if the field is accessible, I add to the queryable fields list.
Here's a full code example, and my current work-around:
// this list of fields are these that will be returned to the component
String[] selectableFields = new String[] { 'Id', 'Name', 'Title', 'Email', 'Fax', 'Phone',
'HomePhone', 'MobilePhone', 'Account.My_custom_field__c', 'MailingStreet',
'MailingCity', 'MailingState', 'MailingPostalCode' };
Map<String, Schema.SObjectField> fieldMap = Schema.SObjectType.Contact.fields.getMap();
List<String> columns = new List<String>();
for(String field : selectableFields) {
if(DEBUG) System.debug('Selectable field: ' + field);
// it's not possible to check accessiblilty on second-level attributes, so just add it to the columns list
if(field.contains('.')) {
columns.add(field.trim());
if(DEBUG) System.debug(' - unknown accessiblity due to second level attribute');
}
else if(fieldMap.get(field).getDescribe().isAccessible()) {
columns.add(field.trim());
if(DEBUG) System.debug(' - accessible');
}
else {
if(DEBUG) System.debug(' - inaccessible');
}
}
// id will also be accessible if has access to object, so check if more than 1 column is accessible
if(columns.size() > 1) {
// get the contact id from the the user's record
String contactId = [select ContactId from user where id = :UserInfo.getUserId() limit 1][0].ContactId;
return Database.query('select ' + String.join(columns, ',') + ' from Contact where id = :contactId');
}
else {
throw new AuraHandledException(NO_ACCESS_MESSAGE);
}
I'd like to avoid having to write if
this else
that style code to handle what I'm calling second-level attributes in the code above, but I don't see any way around it.
I realize I can just hard-code the Account.My_custom_field__c
accessibility check, but I wanted to first see if there are any patterns or best practices to check for field accessibility that take multi-level attributes into account before I do.
Best Answer
I built out a system to do just that for a
FluentQuery
tool I was working on. Here's the relevant bit of theDescribeCache
:And the relevant bit of my
Permissions
class minus all the mocking:The library is open source. Feel free to pull whatever you need out and adjust for your own use.