5

Question

User object-level permissions limit the results of DescribeSObjectResult.getChildRelationships(). Are there any workarounds?

I have submitted an Idea about this (https://success.salesforce.com/ideaView?id=0873A000000CZLzQAO).

(Note: This seems very closely related to the "Fixed" Known Issue In API version 44, DescribeSObjectResult.getChildRelationships() checks Read Access to the child entity and parent relationship field)

Actual goal

I would like to dynamically generate a query on Case, and several child objects. E.g.: SELECT Id, (SELECT Id, CaseId FROM WorkOrders) FROM Case WHERE ... I would like to pass in the child SObjectTypes (WorkOrder.getSObjectType()) without worrying about the exact fields and the names of the relationship. Apex provides exactly one path to get the names of child relationships, and it all starts with SObjectType.Case.getChildRelationships().

However, it looks like this is user-permissions dependent, even in a trigger context, which makes it worthless for my goal.

Description

In Spring '19 (and maybe before -- I have no idea), running getChildRelationships() will not return a relationship if the executing user does not have allowRead permissions on the child SObject.

(I describe this by example, below.)

I believe this is a bug because the documentation says (emphasis mine):

Apex classes and triggers run in system mode. Classes and triggers have no restrictions on dynamically looking up any sObject that is available in the org. You can generate a map of all the sObjects for your org regardless of the current user’s permission, unless you are executing anonymous Apex.

Sample Code

Below is a simple method that finds the relationship between a parentSObjectType and a childSObjectType. It assumes that there is only one relationship between the two and will throw an error otherwise.

public without sharing class SObjectUtils {
  public static ChildRelationship getRelationshipForSObjectType(SObjectType parentSObjType, SObjectType childSObjType) {
    ChildRelationship value;
    for (ChildRelationship rel : parentSObjType.getDescribe().getChildRelationships()) {
      if (childSObjType == rel.getChildSObject()) {
        if (value != null) {
          throw new RelationshipException('Cannot deduce relationship name from ' + parentSObjType + 
                                          '. There is more than one related object of type ' + childSObjType);
        }
        value = rel;
      }
    }
    return value;
  }
}

Elsewhere in a test, I run:

@isTest
private static class TEST_SObjectUtils {

  static User getUserWithoutWorkOrderPermissions() {
    // return a user without workorder object permissions
  }

  static User getUserWithWorkOrderPermissions() {
    // return a user with workorder object permissions
  }

  @isTest
  static void worksForUserWithWorkOrderPermissions() {
    ChildRelationship rel;
    System.runAs(getUserWithWorkOrderPermissions()) {
      Test.startTest();

        rel = SObjectUtils.getRelationshipForSObjectType(
          Case.getSObjectType(), 
          WorkOrder.getSObjectType()
        );

      Test.stopTest();
    }
    System.assertEquals('WorkOrders', rel.getRelationshipName());
  }


  @isTest
  static void worksForUserWithoutWorkOrderPermissions() {
    ChildRelationship rel;
    System.runAs(getUserWithoutWorkOrderPermissions()) {
      Test.startTest();

        rel = SObjectUtils.getRelationshipForSObjectType(
          Case.getSObjectType(), 
          WorkOrder.getSObjectType()
        );

      Test.stopTest();
    }
    System.assertNotEquals(null, rel); // this fails!!
  }
}
  • This seems to be related to this Known Issue – Charles Koppelman Apr 12 at 17:36
  • Out of curiosity, if you directly invoke the unit test while logged in as a user who cannot see Work Order, but don't use System.runAs(), does the observed behavior change? – David Reed Apr 12 at 19:43
  • 1
    @DavidReed Good question -- I think I've spent as much time as I can on this, but it should be fairly easy to test on your end – Charles Koppelman Apr 12 at 20:23
  • @DavidReed It seems you need to be a premier customer to submit a bug report. Is there any way you can submit this? It does seem to render getChildRelationships() useless (even if it works but is untestable, which I do not think is the issue). – Charles Koppelman Apr 17 at 16:17
1

The error here was described in this Known Issue introduce in Winter '19:

In API version 44, DescribeSObjectResult.getChildRelationships() checks Read Access to the child entity and parent relationship field.

The bug is marked Fixed in Spring '19 patch 8.0. My SObjectUtils class was running in v44.0. When I upgrade the API version, the issue was not resolved.

I believe that the field-level security issues described in the above ticket may be fixed, but this bug has to do with object-level security.

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.