8

Let's pretend I am working on a Managed Package app that is soon going to Security Review. I have used all the latest and greatest native features (I know some of them are not GA yet) like as user and WITH USER_MODE to ensure CRUD/FLS and Sharing security.

To avoid overlooking something, I use positive and negative test methods using System.runAs().

Business logic:

public void execute() {
    List<CustomObject__c> records = [SELECT int_Field__C FROM CustomObject__c 
                                    WHERE txt_Field__c = NULL WITH USER_MODE];
    for(CustomObject__c record : packages) {
        record.int_Field__C = calculate(record);
    }
    
    update as user records; 
}

Apex Test:

@IsTest
private static void failsWhenCalledByStandardUser() {

    // Setup
    Exception expectedException = null;

    // Exercise
    try {
        System.runAs(PERSONAS.standardUser) {
            new BusinessLogic().execute();
        }
    }
    catch(Exception ex) {
        expectedException = ex;
    }

    // Verify
    System.assertNotEquals(null, expectedException);
}

Real source code can be found here: https://github.com/rsoesemann/salesforce-isv-cockpit/blob/master/force-app/main/default/classes/SetupPackageNamespaces_Test.cls

Am I right that this indeed should be done to a big extend to finally prove Sharing/CRUD/FLS security of our code?

3 Answers 3

8

I must admit, we have not attempted to revisit this side of things - our core managed package is too large and too mature.

What I do know, from the few uses of System.runAs we have, is that this is incompletely and inconsistently implemented (e.g. it is the user in place at the point that Test.stopTest is invoked that dictates the user for async processing - not the user used when submitting the async processing).

UPDATE:

While the documentation says:

The runAs method doesn’t enforce user permissions or field-level permissions, only record sharing.

@Robert Soesemann has tested this and found the documentation to be inaccurate. According to his tests, CRUD/FLS permissions are also applied.

UPDATE 2:

@Daniel Ballinger (Salesforce PM for Apex) confirmed that the above statement from the documentation was being misinterpreted; CRUD/FLS is applied, but the Apex continues to run in system mode.

This suggests that use of System.runAs could be useful in many (but not all) cases to verify behaviour in different permission scenarios. I say not all because there are known and unknown limitations and gotchas (such as the example above).

10
  • 1
    I think in our package we started using System.runAs and got errors for some missing field-level permissions, but we were using WITH SECURITY_ENFORCED in SOQL
    – ytiq
    Oct 6, 2022 at 14:16
  • 1
    @PhilW I just tested it out here and can confirm the documentation being outdated. I can simulate with System.runAs also the correctness of CRUD and FLS. Oct 6, 2022 at 15:41
  • 1
    I guess we need to report this to the documentation team!
    – Phil W
    Oct 6, 2022 at 15:42
  • 1
    @RobertSösemann is there a repo or example of how System.runAs() isn't aligning with the documentation? Apex running under a test context via runAs() still operates in System Mode. In that respect it doesn't enforce user permissions or field-level permissions. But if you apply the existing security checks such as WITH SECURITY_ENFORCED or the describe calls it will enforce the permissions of the running as user. Oct 6, 2022 at 18:46
  • 4
    Thanks. I understand now and will get the documentation updated to more clearly reflect reality. System.runas() most certainly does enforce the runAs test context users object and field level security as per Enforcing Object and Field Permissions. The distinction is that you don't automatically flip into user mode. Oct 7, 2022 at 0:10
2

I think the documentation is correct: runAs only enforces the sharing rules to be applied for the runAs user, not the FLS. However, if you use 'WITH SECURITY_ENFORCED' in your SOQL and/or Schema describe methods to check field accessibility, that will apply to the currently running user FLS.

In our project we faced unexpected behaviour with FLS with this approach (we expected exactly what you describe) and therefor I did some research on this. This is the result:

  • Apex test always runs in system mode (see all data, see all objects / fields).
  • When using runAs, the code in that block will still run in system mode, but with the exception that record sharing is enforced. The code can only see existing test data that is shared with the runAs user.
  • When using Schema describe methods to check accessibility of an object of field, this will be done in user context (FLS access of current user / runAs user defined in profile and permission sets).
  • The same counts for using WITH SECURITY_ENFORCED. The FLS access of current user / runAs user is checked for the query performed.

All the above 4 statements are also described in the documentation.

0
0

I am getting some value in using System.RunAs(u) to confirm FLS for Users and queries. This is very powerful when the users are in a collection. Then you can loop through the collection, change the System.runAs(u) variable, and test the query for each user.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .