3

Consider the following class:

public inherited sharing class testQueueableSharing implements Queueable {
    public void execute(QueueableContext context) {
        update [SELECT Id FROM User WHERE Name='User User'];
    }
}

Which is invoked from a trigger, by a standard user:

trigger TestTriggerExecutePolicy on Lead (before insert, before update, before delete, after insert, after update, after delete) {
    System.enqueueJob(new testQueueableSharing());
}

Will throw an exception, while declaring class without sharing or calling update from trigger directly succeeds.

Does queueable apex not support inherited sharing? Isn't this something that at least the compiler should catch?

10
  • Ok, funny enough not putting any sharing modified (aka ye olde inherited sharing) does work correctly..?
    – dzh
    Commented Jun 5, 2020 at 22:27
  • 1
    What exception do you get?
    – Raul
    Commented Jun 5, 2020 at 22:28
  • As far as I remember, sharing for an inherited sharing class is by default with sharing if the caller hasn't specified it which in this case is a trigger, whereas code called from a without any declaration of sharing class runs without sharing. Also a best practice is to have a trigger handler call the queueable instead of trigger calling it directly to avoid this side effect.
    – Raul
    Commented Jun 5, 2020 at 22:39
  • @Raul you are wrong on so many ways that I don't even know where to begin.
    – dzh
    Commented Jun 5, 2020 at 22:55
  • 2
    From the doc: If the class is used as the entry point to an Apex transaction, an omitted sharing declaration runs as without sharing. However, inherited sharing ensures that the default is to run as with sharing
    – identigral
    Commented Jun 5, 2020 at 23:05

1 Answer 1

5

This is the proper behavior. Once you request a Future, Queueable, Schedulable, Batchable or cancelable Platform Event, it will be queued for a separate transaction. When that separate transaction runs, a security context will be established at that point in time. If the class is without sharing or not specified, it will run without sharing, but if it is with sharing or inherited sharing, it will be with sharing.

This is because the newly spawned transaction does not remember the previous transaction's security mode; this is not part of the state that is serialized for the transaction. If you need the mode to be preserved, you'll have to do it "manually" by using an execution class with the correct context set (see my answer on a sample implementation).

I would say that this is a bit confusing (compared to not-specified sharing), but it really is meant to, by default, establish that sharing is enabled unless called from a without sharing context. You should treat inherited sharing as with sharing for most practical purposes other than utility classes. This is the polar opposite of the not-specified sharing mode.

4
  • The broader problem is we as an ISV offer a global trigger handler class that spawns queueble job. Previously this queueable class was declared without sharing, but we are going thru a security re-review.
    – dzh
    Commented Jun 7, 2020 at 12:32
  • That's my whole frustration really - I feel like "forgetting" security mode is a bit of a fail, than a feature. For now I'll maintain that running omitted-sharing mode solves my problem. The entry point to this queueable class is only my trigger handler. If people have "Author Apex" perm, their context (according to docs) is going to be with sharing.
    – dzh
    Commented Jun 7, 2020 at 12:35
  • Thanks for the link to the answer, seems like quite an elegant solution which I might try using in future.
    – dzh
    Commented Jun 7, 2020 at 12:37
  • Disregard that. Omitted sharing queueable executed from execute anonymous would run as without sharing... Why they made it so weird.
    – dzh
    Commented Jun 9, 2020 at 7:30

You must log in to answer this question.

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