12

I may need to make many @RestResource Apex classes run either with sharing or without sharing where that option is selected at runtime.

But this approach (that I am using at present):

/**
 * Allow code to run "with sharing" or "without sharing" based on configuration.
 */
public inherited sharing class Sharing {

    public interface Executable {
        Object execute(Object[] args);
    }

    // With
    private with sharing class WithSharing {
        public Object execute(Executable executable, Object[] args) {
            return executable.execute(args);
        }
    }

    // Without
    private without sharing class WithoutSharing {
        public Object execute(Executable executable, Object[] args) {
            return executable.execute(args);
        }
    }

    //
    // Execute the code with or without sharing based on this documentation...
    //
    // Both inner classes and outer classes can be declared as with sharing.
    // The sharing setting applies to all code contained in the class,
    // including initialization code, constructors, and methods.
    // Inner classes do not inherit the sharing setting from their container class.
    //
    public static Object apply(Executable executable, Object[] args) {
        // TODO from config
        Boolean withSharing = false;
        if (withSharing) return new WithSharing().execute(executable, args);
        else return new WithoutSharing().execute(executable, args);
    }

    // No-args
    public static Object apply(Executable executable) {
        return apply(executable, new Object[] {});
    }
}

is quite ugly to add to the many classes (that have static entry points) e.g.:

@RestResource(urlMapping='...')
global inherited sharing class MyRest {

    @HttpPost  
    global static Result post() {
        return (Result) Sharing.apply(new Executable());
    }

    private inherited sharing class Executable implements Sharing.Executable {
        public Object execute() {
            return postWithSharingApplied();
        }
    }

    // The content of the original post method
    private static Result postWithSharingApplied() {
        ...
    }

    ...
}

Is there a cleaner way to do this?

PS

A follow-up question is Inherited sharing question in nested inner classes.

PPS

A key point is that the class containing the business logic MyRest above must not be declared as with sharing or without sharing so is best declared as inherited sharing. (I originally didn't realize this.) This makes each business logic method also have inherited sharing.

The key sentences from the documentation about this are:

The sharing setting of the class where the method is defined is applied, not of the class where the method is called

and:

A class declared as inherited sharing runs as without sharing only when explicitly called from an already established without sharing context.

2 Answers 2

6

Your code is overly complicated because you presumed you needed an inner class in your RestResource class. You don't.

The main problem is that you've reinvented the wheel with Sharing.Executable. There's already an interface that does what you want: Callable.

First, let's rewrite your Sharing class to use Callable:

public class Sharing {
    public abstract class Base {
        public Base() { /* default constructor */ }
        public Object call(Callable source, String action, Map<String, Object> args) {
            return source.call(action, args);
        }
    }
    with sharing class withSharing extends Base { }
    without sharing class withoutSharing extends Base { }
    public static Object apply(Callable source, String action, Map<String, Object> args) {
        // TO-DO From Config
        // 
        Boolean withSharing = true;
        return
            (withSharing? (Base)new withSharing(): (Base)new withoutSharing())
            .call(source, action, args);
    }
}

Now, we can move the code to the top-level class:

@RestResource(urlMapping='...')
global class MyRest implements Callable {
    public Object call(String action, Map<String, Object> args) {
        switch on action {
            when 'POST' { return postWithSharingApplied(); }
            when else { reurn null; }
        }
    }
    public static Result dispatch(String action) {
        return (Result)Sharing.apply(new MyRest(), action, new Map<String, Object>());
    }
    @HttpPost  
    global static Result doPost() {
        dispatch('POST');
    }
    // The content of the original post method
    static Result postWithSharingApplied() {
        ...
    }
    ...
}

The call method adds a bit of overhead for a single method, but will save space with 2 or more methods used. Arguably, it also minimizes the amount of work you need to do once converted to the new format.

Notice, we don't need inherited sharing here at all. The sharing is enforced by the inner classes. You can choose to use the inherited sharing keyword if you'd like, but it has no practical effect from what's written here (although, if you need to pass an AppExchange Security Review, you'll want to use it).

2
  • Thanks for this. If I am reading this correctly, it is using the same technique, but reducing the inner class count and the interface count (though String action, Map<String, Object> args isn't too appropriate here).
    – Keith C
    Commented Jan 21, 2020 at 8:34
  • @KeithC Right. The cost for a single action is slightly higher, but if you need to handle multiple actions, it's more efficient (as minimum as 2 is fine). This is mostly a limitation of Apex, but I felt it might help you.
    – sfdcfox
    Commented Jan 21, 2020 at 8:37
1

The thing is, sharing mode in Apex only really affects SOQL, DML, and other Apex methods that do query/DB-like work. So perhaps you could centralize these operations in a core class that can be explicitly called in with/out sharing mode. Then all of your dual-mode classes can default to with sharing, but when a sharing-sensitive operation is called you route it through the core class with an explicit sharing mode parameter.

1
  • Good thought Charles. My problem is that this is old code with several dozen REST API classes that I want to limit the changes to. And these more or less all read and write data..
    – Keith C
    Commented Jan 23, 2020 at 17:57

You must log in to answer this question.

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