9

Has anyone tried using the new user-level access DML operations with the automated process user, say in a platform event trigger?

It seems as though calling Database.insert(records, AccessLevel.USER_MODE) results in an exception be thrown on insert because the user seemingly has no access to any fields on the object - I'd expect it to have access to everything still.

I've done some basic experiements in a brand new scratch org and it seems like it does lose access to fields, which for ISVs is not great if every query/DML operation needs to check which user is running the code prior to using these new features.

The Experiment

Setup

I created a platform event, called ObjRequest__e and added a custom 255 text field called ObjName__c.

I also created a custom object called Obj__c, that has a standard Text name field, and one custom Datetime field called Stamp__c, with FLS completely cleared out so no user has access to it.

I created a simple trigger for the platform event that will run as the Automated Process user:

trigger ObjRequest_AfterInsert on ObjRequest__e (after insert) {
    for(ObjRequest__e event : Trigger.New) {
        if(String.isNotBlank(event.ObjName__c)) {
            Objs.insertRecord(event.ObjName__c);
        }
    }
}

... and a class with a single static method that inserts Obj__c records with the name given, automatically setting the Stamp__c field:

public with sharing class Objs {

    public static void insertRecord(String name) {
        Obj__c o = new Obj__c(name = name, Stamp__c = Datetime.now());

        try {
            Database.insert(o, AccessLevel.USER_MODE);
        } catch (DMLException e) {
            System.debug(e.getMessage());
            for(Integer i = 0; i < e.getNumDml(); i++) {
                System.debug(e.getDmlFieldNames(i));
            }
        }
    }
}

Test

To test functionality as each user I ran this code via execute anonymous:

Objs.insertRecord('test' + Datetime.now().time());
EventBus.publish(new List<ObjRequest__e>{new ObjRequest__e(ObjName__c = 'test' + Datetime.now().time())});

The logs for the running user contained this, which is to be expected:

07:04:07.189 (220462445)|USER_DEBUG|[9]|DEBUG|Operation failed due to fields being inaccessible on Sobject Obj__c, check errors on Exception or Result! 
07:04:07.189 (220654470)|USER_DEBUG|[11]|DEBUG|(Stamp__c)

The logs for the Automated Process user contain the same thing:

07:04:08.1 (26859159)|USER_DEBUG|[9]|DEBUG|Operation failed due to fields being inaccessible on Sobject Obj__c, check errors on Exception or Result! 
07:04:08.1 (27091582)|USER_DEBUG|[11]|DEBUG|(Stamp__c)

So it appears as though enforcing user mode on DML strips the Automated Process user of it's system-level access, which is a bit of a downer from the POV of trying to simplify code if we now need special handling for code that might get run by that user.

As a final quick test I updated FLS to grant write access on Stamp__c to all profiles. Now when running the same exec anon snippet, the record for the running user is created no problem, but the Automated Process user fails to insert with the same exception as before.

Are my tests valid? Is this how this feature should work?

1
  • very similar to this Known Issue: issues.salesforce.com/issue/a028c00000zHxy3AAC/… The KI says "Also, it's not possible to assign the Permission Set to the user Automated Process user..." which is not true; the PermissionSetAssignment record can be created via Apex, but that does not seem to help.
    – SeanW
    Commented Jul 25 at 14:36

3 Answers 3

5

I raised this with Salesforce support, and it's gone to the Apex team and back, and is apparently working as designed. The feature wasn't built to support the Automated Process user and as a result, the only real fix here will be to use permission set elevation, or to try and detect when the automated process user is the running user and subsequently avoid user mode for queries or DML operatoins.

4
  • 1
    Would using a Platform Event Subscriber help in this situation? I had a similar problem with GSU not having the correct permissions, and that was the suggestion here as an option? salesforce.stackexchange.com/questions/414382/… Commented Jan 3 at 13:57
  • 1
    Doesn't that "other user" mean the automated process user? Even if you can specify a specific user it'd still be a bit cumbersome as this is for a managed package, I'd probably have to create a trigger/flow in each target org with the appropriate configuration.
    – Matt Lacey
    Commented Jan 3 at 13:59
  • 1
    If you use this config, it can be any user I believe: developer.salesforce.com/docs/atlas.en-us.api_meta.meta/… But yes., not sure how that could\would work with Packages - feel like it might have to be a post install step - might be worth asking in Partner Portal Commented Jan 3 at 14:46
  • 2
    Oh I'd totally missed those - they could be very handy for us, especially as we do have issues with things being owned by the automated process user
    – Matt Lacey
    Commented Jan 3 at 15:44
2

We had a similar issue and we solved it using flows, although this only works if the user that publishes the platform event has the necessary permissions.

Flows that susbcribe to platform events run as the user that published the platform event.

We moved the trigger logic into an invokable class, and used a flow to subscribe to the platform event channel.

This resolved all our issues with this.

2
  • I was going to say that wouldn't work for us as we do a lot of dynamic queries etc., but by still invoking the same code via an invocable that might work. Flows still don't seem to have the best performance in prod but I'll definitely keep this in mind as a backup option
    – Matt Lacey
    Commented Dec 7, 2023 at 11:00
  • Thinking more - this won't work in this instance because the user publishing the event is a site guest user, hence why I publish the event in the first place - it allows me to process things without opening access up to that user.
    – Matt Lacey
    Commented Jan 3 at 13:40
0

I believe this is a bug with AccessLevels. Until it's fixed, you can use the following workaround:

List<Obj__c> originalObjs;
List<Obj__c> strippedObjs = Security.stripInaccessible(AccessType. CREATABLE, originalObjs).getRecords();
Database.insert(strippedObjs, AccessLevel.USER_MODE);

More info: https://salesforce.stackexchange.com/a/412743/61394

2
  • Buti f this was running as the auto proc user woudln't it still end up with zero access to the fields involved?
    – Matt Lacey
    Commented Dec 7, 2023 at 10:59
  • I have found that running Database.insert() in USER_MODE fails with that error even for a System Admin with write access to every field in the object. That is why I think this is a bug. Commented Dec 7, 2023 at 14:19

You must log in to answer this question.

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