0

org.auraframework.throwable.quickfix.InvalidDefinitionException: Invalid definition for null:SBQQ__Subscription__c[]: Access to entity 'SBQQ__Subscription__c' denied

I am running into an issue where a user who tries to access a custom object is getting hit with an error from a lightning component. I would like to not display the component if the user does not have the correct permissions to one of the objects being used.

Is this possible?

I am stuck on figuring out how exactly a component can be hidden or 'unrendered' based on permissions. Would this be a simple aura:if? and setting a boolean based on permission?

The component itself is on a custom object, the component does not actually use anything other than the recordId. It queries a sub-object which is part of CPQ or a managed package.

LAUNCHER COMPONENT:

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" >
    <aura:attribute name="recordId" type="String" />
    <aura:attribute name="isRendered" type="Boolean" default="false" /> 

    <aura:handler name="HideAddProductInScopeToProjectEvent" event="c:HideAddProductInScopeToProjectEvent"
        action="{!c.toggleShowComponent}"/>

    <div class="slds-box slds-theme_default">
        <div class="slds-grid">
            <header class="slds-media slds-media_center slds-has-flexi-truncate">
              <div class="slds-media__figure">
                <lightning:icon iconName="custom:custom60" size="small"/>
              </div>
              <div class="slds-media__body">
                <h2>
                  <a href="javascript:void(0);" class="slds-card__header-link slds-truncate" title="Products In Scope">
                    <span class="slds-text-heading_small">Add Products In Scope</span>
                  </a>
                </h2>
              </div>
            </header>
            <lightning:button variant="neutral" label="Launch" onclick="{!c.toggleShowComponent}" />
        </div>
    </div>

    <aura:if isTrue="{!v.isRendered}">
        <div>
            <c:AddProductInScopeToProject recordId="{!v.recordId}" />    
        </div>
    </aura:if>  
</aura:component>

MODAL COMPONENT: CONTAINS 2 LIST COMPONENTS

<aura:component implements="force:hasRecordId" access="global" controller="AddProductInScopeToProjectCtrl">    
    <aura:attribute name="recordId" type="String" />
    <aura:attribute name="isSubscriptionListRendered" type="Boolean" default="true" /> 

    <aura:registerEvent name="HideAddProductInScopeToProjectEvent" type="c:HideAddProductInScopeToProjectEvent"/>

    <div style="height: 640px;">
        <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open slds-modal_medium">
            <div class="slds-modal__container">
                <header class="slds-modal__header">
                    <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close" onclick="{!c.hideComponent}">
                        <lightning:icon iconName="utility:close" size="small" variant="inverse" />
                        <span class="slds-assistive-text">Close</span>
                    </button>
                    <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Add Products In Scope</h2>
                    <p class="slds-m-top_x-small">Only <b>Subscriptions</b> and <b>Assets</b> which are not associated with an existing Product In Scope will show up here.</p>
                </header>
                <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">              
                    <div class="slds-grid slds-m-top_medium">
                        <div class="slds-button-group" role="group">
                            <button class="slds-button slds-button_neutral" onclick="{!c.showSubscriptionListComponent}">Subscriptions</button>
                            <button class="slds-button slds-button_neutral" onclick="{!c.showAssetListComponent}">Assets</button>
                        </div>
                    </div>                    
                    <aura:if isTrue="{!v.isSubscriptionListRendered}">  
                        <c:AddProductInScopeToProjectSubscriptionList aura:id="subscriptionListComponent" recordId="{!v.recordId}" />
                        <aura:set attribute="else">
                            <c:AddProductInScopeToProjectAssetList aura:id="assetListComponent" recordId="{!v.recordId}" />
                        </aura:set>
                    </aura:if>
                </div>
                <footer class="slds-modal__footer">
                    <button class="slds-button slds-button_neutral" onclick="{!c.hideComponent}">Cancel</button>
                    <button class="slds-button slds-button_brand" onclick="{!c.createProductInScopeForProject}">Create</button>
                </footer>
            </div>
        </section>
        <div class="slds-backdrop slds-backdrop_open"></div>
    </div>

    <!-- Display Lightning Data Service errors, if any -->
    <aura:if isTrue="{!not(empty(v.recordError))}">
        <div class="recordError">
            {!v.recordError}
        </div>
    </aura:if>  

</aura:component>

CONTROLLER TO RUN IT ALL:

public class AddProductInScopeToProjectCtrl {

    //Subscriptions
    @AuraEnabled
    public static List<SBQQ__Subscription__c> getSubscriptionsForAccount(Id accountId){
        if(accountId == null) return null;

        if(!Schema.SObjectType.ProjectProduct__c.isAccessible()) return null;
        if(!Schema.SObjectType.SBQQ__Subscription__c.isAccessible()) return null;

        String [] projectProductAccessFields = new String [] {'Id',
            'Subscription__c',
            'Account__c'
            };

        Map<String,Schema.SObjectField> ppFieldMap = Schema.SObjectType.ProjectProduct__c.fields.getMap();

        for (String fieldToCheck : projectProductAccessFields) {
          // Check if the user has access to view field
          if (!ppFieldMap.get(fieldToCheck).getDescribe().isAccessible()) {
               throw new System.NoAccessException();
            }
         }

        List<ProjectProduct__c> productsInScopeByAccount = [SELECT Id, Subscription__c FROM ProjectProduct__c WHERE Account__c = :accountId];
        Set<Id> subscriptionIds = new Set<Id>();
        for(ProjectProduct__c pis : productsInScopeByAccount){
            subscriptionIds.add(pis.Subscription__c);   
        }

        String [] subscriptionAccessFields = new String [] {'Id',
            'Name',
            'SBQQ__Product__c',
            'SBQQ__ProductName__c',
            'Product_Code_REF__c',
            'SBQQ__Quantity__c',
            'SBQQ__StartDate__c',
            'SBQQ__EndDate__c',
            'SBQQ__TerminatedDate__c',
            'SBQQ__Account__c'
            };

        Map<String,Schema.SObjectField> subFieldMap = Schema.SObjectType.SBQQ__Subscription__c.fields.getMap();

        for (String fieldToCheck : subscriptionAccessFields) {
          // Check if the user has access to view field
          if (!subFieldMap.get(fieldToCheck).getDescribe().isAccessible()) {
               throw new System.NoAccessException();
            }
         }      

        return [SELECT Id, Name, SBQQ__Product__c, SBQQ__ProductName__c, Product_Code_REF__c, SBQQ__Quantity__c, SBQQ__StartDate__c, SBQQ__EndDate__c, SBQQ__TerminatedDate__c FROM SBQQ__Subscription__c 
                WHERE SBQQ__Account__c = :accountId AND Id NOT IN :subscriptionIds];
    }

    @AuraEnabled
    public static void createProductInScopeBySubscription(Id projectId, Id accountId, List<SBQQ__Subscription__c> subscriptions){
        if(subscriptions == null || subscriptions.isEmpty()) return;
        if(!Schema.SObjectType.ProjectProduct__c.isAccessible() || !Schema.SObjectType.ProjectProduct__c.isCreateable()) return;
        if(!Schema.SObjectType.SBQQ__Subscription__c.isAccessible()) return;

        List<ProjectProduct__c> pisToInsert = new List<ProjectProduct__c>();
        for(SBQQ__Subscription__c sub : subscriptions){
            ProjectProduct__c pis = new ProjectProduct__c();
            pis.Account__c = accountId;
            pis.Project__c = projectId;
            pis.Subscription__c = sub.Id;
            pis.Quantity__c = sub.SBQQ__Quantity__c;
            pisToInsert.add(pis);
        }
        insert pisToInsert;
    }

    //Assets
    @AuraEnabled
    public static List<Asset> getAssetsForAccount(Id accountId){
        if(accountId == null) return null;
        if(!Schema.SObjectType.ProjectProduct__c.isAccessible()) return null;
        if(!Schema.SObjectType.Asset.isAccessible()) return null;


        String [] projectProductAccessFields = new String [] {'Id',
            'Asset__c',
            'Account__c'
            };

        Map<String,Schema.SObjectField> ppFieldMap = Schema.SObjectType.ProjectProduct__c.fields.getMap();

        for (String fieldToCheck : projectProductAccessFields) {
          // Check if the user has access to view field
          if (!ppFieldMap.get(fieldToCheck).getDescribe().isAccessible()) {
               throw new System.NoAccessException();
            }
         }

        List<ProjectProduct__c> productsInScopeByAccount = [SELECT Id, Asset__c FROM ProjectProduct__c WHERE Account__c = :accountId];
        Set<Id> assetIds = new Set<Id>();
        for(ProjectProduct__c pis : productsInScopeByAccount){
            assetIds.add(pis.Asset__c);   
        }

        String [] assetAccessFields = new String [] {'Id',
            'Name',
            'Product2Id',
            'ProductName__c',
            'ProductCode',
            'Quantity',
            'AccountId'
            };

        Map<String,Schema.SObjectField> asstFieldMap = Schema.SObjectType.Asset.fields.getMap();

        for (String fieldToCheck : assetAccessFields) {
          // Check if the user has access to view field
          if (!asstFieldMap.get(fieldToCheck).getDescribe().isAccessible()) {
               System.debug('No Access to field: ' + asstFieldMap.get(fieldToCheck).getDescribe());
               throw new System.NoAccessException();
            }
         }  

        return [SELECT Id, Name, Product2Id, ProductName__c, ProductCode, Quantity FROM Asset WHERE AccountId = :accountId AND Id NOT IN :assetIds];
    }

    @AuraEnabled
    public static void createProductInScopeByAsset(Id projectId, Id accountId, List<Asset> assets){
        if(assets == null || assets.isEmpty()) return;
        if(!Schema.SObjectType.ProjectProduct__c.isAccessible() || !Schema.SObjectType.ProjectProduct__c.isCreateable()) return;
        if(!Schema.SObjectType.Asset.isAccessible()) return;

        List<ProjectProduct__c> pisToInsert = new List<ProjectProduct__c>();
        for(Asset asst : assets){
            ProjectProduct__c pis = new ProjectProduct__c();
            pis.Account__c = accountId;
            pis.Project__c = projectId;
            pis.Asset__c = asst.Id;
            pis.Quantity__c = asst.Quantity;
            pisToInsert.add(pis);
        }
        insert pisToInsert;
    }
}
  • 2
    It is certainly possible. What have you tried so far? Have you done any investigation in Apex to see if you can determine that access on the back end? It will be easier to guide you if you clearly indicate where specifically you are stuck. – Adrian Larson Apr 9 '18 at 21:48
  • I have not really tried anything, I know we could identify whether not a user has access to an object but how can we translate that to rendering a component. It seems like it throws this error the moment the record page is access. It doesn't load anything. I was thinking perhaps the 'component visibility' functionality from the lightning page builder, but it didn't seem to have anything. – Coova Apr 9 '18 at 21:51
  • 1
    Are you familiar with the <aura:if> tag? Are you using it? What does your code look like? Can you give the basic gist? – Adrian Larson Apr 9 '18 at 21:53
  • You could store the object as a regular Object or Map which I find gets around some these issues sometimes. – Caspar Harmer Apr 9 '18 at 23:05
  • Here's a document to jumpstart your understanding of how you might tackle this implementation in your component: Lightning Security CRUD/FLS – Mark Pond Apr 9 '18 at 23:13
0

In your init, run a query similar to :

SELECT HasReadAccess, Haseditaccess, hasdeleteaccess, RecordId FROM UserRecordAccess WHERE RecordId =: recordId AND UserId =: u.Id

Then use an aura:if to decide whether or not to display the componenent

  • 1
    Unfortunately, that won't work; the problem is that the user has no access to the object itself, so a query like that won't help. – sfdcfox Apr 9 '18 at 22:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.