apex-enterprise-patterns / fflib-apex-common

Common Apex Library supporting Apex Enterprise Patterns and much more!
BSD 3-Clause "New" or "Revised" License
913 stars 517 forks source link

Allow overwriting of enforceCRUD and enforceFLS Selector flags #280

Closed nicholas-getpraxis closed 4 years ago

nicholas-getpraxis commented 4 years ago

We experienced an issue where SELECTORS (classes extending the fflib_SObjectSelector base class) were enforcing CRUD when running in an InvocableMethod, with no way to update. I do understand that this is the intended behaviour, where enforceCRUD is defaulted to true, enforceFLS is defaulted to false, and the Configuration.disableTriggerCRUDSecurity() method is available in DOMAINS to allow it to be overriden. The issue here is that the Configuration.disableTriggerCRUDSecurity() is not available in regular service classes that don't implement/extend an fflib interface/class.

To resolve, we updated the fflib_SObjectSelector base SELECTOR class and overloaded the newQueryFactory() and addQueryFactorySubselect() methods to allow the explicit assignment of the enforceCRUD and enforceFLS flags when creating an instance of a SELECTOR. Though newQueryFactory() did have a method that allowed those flags to be set, as it required other "unrelated" flags to be set, we elected to overload with a more purpose-driven version.

Though we did resolve, we're opening this issue to raise an "Update Ticket" of sorts have those method overloads be a part of the core package, so when the time comes that we want to upgrade our version of FFLIB, we can do so without issue.

Our Local overloaded methods in the fflib_SObjectSelector base class:

     /**
     * Returns a QueryFactory configured with the Selectors object, fields, fieldsets and default order by
     **/
    public fflib_QueryFactory newQueryFactory(Boolean assertCRUD, Boolean enforceFLS)
    {    
        return newQueryFactory(assertCRUD, enforceFLS, true);
    }

     /**
     * Adds a subselect QueryFactory based on this selector to the given QueryFactor, returns the parentQueryFactory
     **/
    public fflib_QueryFactory addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory, Boolean assertCRUD, Boolean enforceFLS)
    {    
        fflib_QueryFactory subSelectQueryFactory = 
            parentQueryFactory.subselectQuery(getSObjectType2());
        return configureQueryFactory(
            subSelectQueryFactory, 
            assertCRUD, 
            enforceFLS,
            true);
    }
daveespo commented 4 years ago

I'm not sure I'm following. There are already overloaded constructors for fflib_SObjectSelector

So if your Selector subclass doesn't want to enforce CRUD or FLS, you can just delegate your constructor to the overloaded parent constructor

example:

public class AccountsSelector extends fflib_SObjectsSelector{
  public AccountsSelector(){
    super(false,false,false);
  }
}
Javwadazeem commented 4 years ago

@nicholas-getpraxis , I think we have a method in fflib_SObjectSelector to ignoreCRUD. I think this might help you. Method Name public fflib_SObjectSelector ignoreCRUD() { this.m_enforceCRUD = false; return this; } Database.query(new AccountSelector().ignoreCRUD().newQueryFactory().toSoql());

P.S- You can follow the @daveespo approach or you can use above approach as well.

nicholas-getpraxis commented 4 years ago

@daveespo I did neglect looking at overriding the constructor of the SELECTOR subclass, but that method still wouldn't have worked for us because we'd want to keep the enforcement of CRUD & FLS at the method level.

@Javwadazeem
This looks closer to what we're looking for. I'll test the above approach, specifically with the addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory) method.

I'm assuming the SELECTOR method would be along the lines of:

// Query Factory for this Selector (Account)
fflib_QueryFactory AccountQueryFactory = new AccountSelector().ignoreCRUD().newQueryFactory();

// Add a query sub-select via the Query Factory for the Contact object
fflib_QueryFactory ContactQueryFactory =
      new ContactSelector()
      .ignoreCRUD()
      .addQueryFactorySubselect(AccountQueryFactory);

// Set the condition and build the query
return (List<Account>) Database.query(
      AccountQueryFactory
      .setCondition('Id IN:IdSet')
      .toSOQL());
nicholas-getpraxis commented 4 years ago

@Javwadazeem It worked.

I did make one small tweak. Changed fflib_QueryFactory AccountQueryFactory = new AccountSelector().ignoreCRUD().newQueryFactory(); to fflib_QueryFactory AccountQueryFactory = this.ignoreCRUD().newQueryFactory();

Thanks alot.