2

The need:

An external system is querying SF data via an Apex REST API. It authenticates to SF via an integration user account (an M2M integration). We need the data returned by the API to be filtered based on row-level access for a specified end user (userid is a required filter for the GET method). Currently this would only apply to internal users, but we hope to add Community Users for external Contacts that request this data from the external system as well.


Possible solutions & their challenges:

  1. Write custom code that simulates “with sharing” for a specified User Id by querying the pertinent tables (Share, GroupMember, etc.). This would be like hand-rolling the sharing aspect of System.runAs(), but for functional code.
  • This is a lot of code to write and maintain
  • Likely performance hit (the external system is sensitive to increased latency)
  1. Find a way to authenticate to SF AS THE END USER that corresponds to the external system’s user, so that “with sharing” can be meaningfully applied to the Apex REST class.
  • This approach involves many unknowns and security challenges. It would take months working with our InfoSec and Mulesoft teams (POC, security reviews, etc.) to determine if this is a viable approach for us.
  1. [A SOLUTION I HAVEN’T YET CONSIDERED??]

Stubbed code example (for visualization purposes):

@RestResource(urlMapping='/v1/proposals/*')
global without sharing class wmp_ProposalsURIv1 {
  
    @HttpGet
    global static ProposalsPayload getProposals() {
        String userId = RestContext.request.params.get('userid');

        // Simulate "with sharing" for this user in subsequent queries
        // (involves querying Share, GroupMember, etc. tables)
    }
    ...
}

1 Answer 1

3

The ideal way to do it is to authenticate as the actual user. We're told in no uncertain terms that we should not try to "roll our own implementation of security," mostly because salesforce.com has already built the infrastructure you need to do it right, and leveraging it should be relatively easy. Of course, this means getting rid of the integration user for those use cases. Realistically, I don't know about your external system, but the easiest way is likely to use Salesforce as the IdP (Identity Provider) using OAuth SSO. This can simultaneously give you access to the external system and a token that can be used for Salesforce REST access.

4
  • Thanks, @sfdcfox, that's what I was afraid of. Although I agree it's the best way to do it, our group has all but ruled out this option (due to the timing required to get all the relevant Enterprise groups involved). Do you happen to know the relative query performance of "rolling your own" security (let's say a simplified/basic version of it, e.g. 1 Group deep) vs. using SF's "with sharing" mechanism? If not, I may POC to see if latency will become an issue with the "home-rolled" route Commented May 26, 2021 at 17:17
  • @Bow-chicawow-ers The main concerns are governor limits, you only get 50k rows and 100 queries, and a large hierarchy/sharing model could easily blow up this limit. One can emulate sharing "perfectly", but at some point, it will cost too much, so scalability may be a problem. With some clever engineering, you could probably handle it, but I personally wouldn't want to be the one to build this. The latency is probably the least of your concerns.
    – sfdcfox
    Commented May 26, 2021 at 17:40
  • In our case we're not querying large data volumes nor needing "perfect"/exhaustive sharing replication. This is for real-time display of limited-size data collections in an external system's list UI (not DB replication or a LDV scenario like that), and our sharing filters need only to be applied to the base query object (Case); we only need to reference Share tables once, and querying data points from all related records can be done in system mode Commented May 27, 2021 at 19:43
  • My concern about latency is based on the fact that we have 12mm+ CaseShare rows. But perhaps the time it takes to serialize & send the data over the wire to the consumer of our Apex REST API will dwarf the added overhead of joining CaseShare and GroupMember tables Commented May 27, 2021 at 19:44

You must log in to answer this question.

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