Here's the scenario. Customer Community user creates a record. A trigger fires, calls into a without sharing class which inserts an AccountShare record. AccountShare is not accessible to community users, thus without sharing. If I remove without sharing, indeed I get an error message that AccountShare is not available.

With without sharing, I'm getting 'INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY'. There are 2 Ids on the AccountShare. I verified the AccountId is correct, and currently has no manual shares against it. The UserOrGroupId is an Id of a Customer Community Plus user, which are allowed to participate in Roles and Sharing, unlike non-Plus community users. see bottom of this page

I can anonymous execute the Apex that creates this AccountShare and it works fine. It seems that the code only fails when running on the Community side, without sharing notwithstanding. I don't see why I'm getting the INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY. Is there something I'm missing?

Edit: The user that is the subject of the share is not the owner of the Account.

  • What type of community license do you have? Apex managed sharing is only available for Partner Community and Community Plus licenses. – crmprogdev Mar 2 '17 at 17:48
  • The users I'm attempting to share the record to are Customer Community Plus users. – Jeremy Ross Mar 2 '17 at 18:27

Without seeing your code, it's a bit difficult to fully diagnose your issues. I can share some of my experience with apex managed sharing in community plus orgs and suggestions on architecture that you may find helpful.

First, let me say that while your transaction may have been initiated from the community side, Apex triggers and other code runs from within your org and is agnostic with respect to where the transaction was initiated from. Where it isn't though is with respect to any sharing permissions the user who initiated the transaction might have when with sharing is taken into consideration. There's a huge difference between the permissions you have running the code from the Dev Console and when running it as a Community User.

What I have typically done when writing managed sharing code is:

  1. Use a trigger to write the transaction into a custom object
  2. A trigger on the custom object then calls a queueable
  3. The queueable queries the custom object for unshared records

    Note: Use the same custom object/queuable for ALL sharing. The queueable can act as a dispatcher to call other queuables specific to the type of record.

  4. The sharing calculations are performed

  5. Any shares that might need removed are also calculated
  6. If successful, the record is deleted from the custom object
  7. If errors, a code/message is written to the record
  8. If previous error exists, email is sent to admin notifying record needs manual intervention
  9. Class is scheduled to run again for 2nd attempt to calculate sharing

Note: Instead of using a trigger on the custom object, a batch job can be scheduled at regular intervals to process all the records similar to what I've described. It all depends on how responsive you need the record sharing mechanism to be. In most cases, 24 hours is sufficient, but in others, it may need to be done hourly.

There are many benefits to doing this asynchronously. Typically, all the shares on the object will need to be recalculated. This can be very time consuming and process intensive. You don't want to "trigger" a complete recalculation of the sharing table for every single record if this occurs multiple times/day. Doing so can create problems with record locks and record contention.

Moving this into a batch or queuable job, moves the calculations away from the context of the original sharing permissions of the user who initiated the action. This is now completely out of the original execution context that started the process. In my experience, only large communities use a community plus license which means you're likely to have many records you'll be sharing. That also means you'll have a very large sharing table which you won't want to have recalculated multiple times/day which takes time and resources.

This pattern is also very scalable.

Your Answer

