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?

dzh
  • 4,106
  • 7
  • 50
  • 99
  • Ok, funny enough not putting any sharing modified (aka ye olde inherited sharing) does work correctly..? – dzh Jun 05 '20 at 22:27
  • 1
    What exception do you get? – Raul Jun 05 '20 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 Jun 05 '20 at 22:39
  • @Raul you are wrong on so many ways that I don't even know where to begin. – dzh Jun 05 '20 at 22:55
  • Exception is of "insufficient permissions" which doesn't matter as it's just to illustrate the wider problem. – dzh Jun 05 '20 at 22:56
  • 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 Jun 05 '20 at 23:05
  • @identigral You should have added an answer, really. – sfdcfox Jun 05 '20 at 23:21
  • @dzh: apart from the wording in my comment, I was not wrong afterall. :) – Raul Jun 06 '20 at 10:30
  • @Raul omitted sharing classes run in without-sharing mode only when called from Visualforce, Lightning, etc, but when called from anonymous apex (the only real attack vector, if you think about it) inherits with sharing. – dzh Jun 07 '20 at 12:26
  • @Raul my queueable class is indeed called from a trigger handler (declared as inherited sharing) which still exhibits the same behaviour. – dzh Jun 07 '20 at 12:29

1 Answers1

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.

sfdcfox
  • 489,769
  • 21
  • 458
  • 806
  • 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 Jun 07 '20 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 Jun 07 '20 at 12:35
  • Thanks for the link to the answer, seems like quite an elegant solution which I might try using in future. – dzh Jun 07 '20 at 12:37
  • Disregard that. Omitted sharing queueable executed from execute anonymous would run as without sharing... Why they made it so weird. – dzh Jun 09 '20 at 07:30