0

I'm trying to create an Apex Condition on a Transaction Security Policy. The code below is working to limit large (>250 rows) exports to users with the System Administrator profile:

global class BlockLargeDataExportEventCondition implements TxnSecurity.EventCondition {
public boolean evaluate(SObject event) {
    switch on event{
        when ReportEvent reportEvent {
            return evaluate(reportEvent);
        }
        when null {
            // Don't take policy action when event is null
            return false;
        }
        when else{
            // Don't take policy action when event is not handled
            return false;
        }
    }
}
/**
 * Handle evaluating ReportEvent
 */
private boolean evaluate(ReportEvent reportEvent){
    Profile profile = [SELECT Name FROM Profile WHERE Id IN
                        (SELECT profileId FROM User WHERE Id = :reportEvent.UserId)];
    // Take policy action only if the user profile is not 'System Administrator' and
    // RowsProcessed greater than 250.
    if (!profile.Name.contains('System Administrator')
        && ReportEvent.Operation.equals ('ReportExported')
        && reportEvent.RowsProcessed > 250) {
        return true;
    }
    return false;
}

I'm trying to figure out how to also allow users with a specific permission set assignment to export large reports. I have tried the below code but it's not working:

global class BlockLargeDataExportEventCondition implements TxnSecurity.EventCondition {
public boolean evaluate(SObject event) {
    switch on event{
        when ReportEvent reportEvent {
            return evaluate(reportEvent);
        }
        when null {
            // Don't take policy action when event is null
            return false;
        }
        when else{
            // Don't take policy action when event is not handled
            return false;
        }
    }
}
/**
 * Handle evaluating ReportEvent
 */
private boolean evaluate(ReportEvent reportEvent){
    Profile profile = [SELECT Name FROM Profile WHERE Id IN
                        (SELECT profileId FROM User WHERE Id = :reportEvent.UserId)];
    List<AggregateResult> result = [SELECT count(Id) FROM PermissionSetAssignment WHERE AssigneeId = :Userinfo.getUserId() AND PermissionSet.Name = 'Export_Reports'];
    System.debug(LoggingLevel.INFO, '>>> result' + result);
    Integer x = (Integer) result[0].get('total');
    System.debug(LoggingLevel.INFO, '>>> x' + x);
    boolean hasExportPermissions = false;
    if (x > 0) {
        hasExportpermissions = true;
        }
    System.debug(LoggingLevel.INFO, 'hasExportPermissions' + hasExportPermissions);
    // Take policy action only if the user profile is not 'System Administrator' 
    // AND user does not have 'Export Reports' permission set AND
    // RowsProcessed greater than 250.
    if (!profile.Name.contains('System Administrator')
        && !hasExportPermissions
        && ReportEvent.Operation.equals ('ReportExported')
        && reportEvent.RowsProcessed > 250) {
        return true;
    }
    return false;
}

}

Part of the problem is I can't figure out how to generate a debug log for this, so I can't see any of my System.debug statements. I have a user trace flag set up on my user and it generates logs for some things (I see a log every time I edit the Apex code, for example), but it does not generate a log when I export a report. Any idea what I'm doing wrong with the debug logs, OR any idea what is wrong with my code and how I would create the condition to do what I'm trying to do? (allow users with Permission set assignment 'Export_Reports' OR Profile = 'System Administrator' to export large reports, but block those exports for all other users)

2 Answers 2

2

You should not be checking if someone has a Permission Set assigned or not. That is a paradigm mismatch with the more standard Salesforce approach. You should instead check if they have a Custom Permission assigned. Where it comes from should be irrelevant, even if it is indeed via that Permission Set. Checking Custom Permission instead is significantly simpler.

Boolean hasExportPermissions = FeatureManagement.checkPermission('Export_Reports');
4
  • thanks! that makes the code much simpler! I'm struggling to get tests written right now so hopefully that will simplify that as well
    – rg_
    Oct 12, 2022 at 17:05
  • It most certainly will, especially if you realize you can just bind it to a @TestVisible static Boolean variable, which you can then set from test methods to exactly control behavior.
    – Adrian Larson
    Oct 12, 2022 at 17:11
  • I'm having trouble getting this to work. I created a custom permission called Export_Reports, added it to a permission set and assigned a user to it. But using the line of code you suggested in my Condition class is returning false. Is there something else I need to do to ensure that FeatureManagement.checkPermission is checking permissions for the user exporting the report as opposed to any other user? I placed that line inside the private boolean evaluate(ReportEvent reportEvent){ function, just below the code that fetches the Profile. Did it need to go somewhere else? Thank you!
    – rg_
    Oct 12, 2022 at 18:46
  • It should just go in the same place you already computed hasExportPermissions. This FeatureManagement method only works for the running user.
    – Adrian Larson
    Oct 12, 2022 at 19:06
1

Somebody answered the debug logs question in another forum so I'll post it here too in case anybody else is trying to figure this out. The problem was the debug log should be set up for the Automated Process User, not the user attempting the export. Once I set up that log I was able to debug. I also had some errors in the code above (should have been AssigneeId = :reportEvent.UserId instead of :Userinfo.getUserId()).

The final code that worked for this (checking for a permission set as well as a profile) was this

global class BlockLargeDataExportEventConditionPERM implements TxnSecurity.EventCondition {
    public boolean evaluate(SObject event) {
        switch on event{
            when ReportEvent reportEvent {
                return evaluate(reportEvent);
            }
            when null {
                // Don't take policy action when event is null
                return false;
            }
            when else{
                // Don't take policy action when event is not handled
                return false;
            }
        }
    }
    /**
     * Handle evaluating ReportEvent
     */
    private boolean evaluate(ReportEvent reportEvent){
        // get profile
        Profile profile = [SELECT Name FROM Profile WHERE Id IN
                            (SELECT profileId FROM User WHERE Id = :reportEvent.UserId)];
        System.debug(LoggingLevel.INFO, 'profile' + profile);
        
        // check if user has Export_Reports permission set
        List<AggregateResult> result = [SELECT count(Id) FROM PermissionSetAssignment WHERE AssigneeId = :reportEvent.UserId AND PermissionSet.Name = 'Export_Reports'];
        System.debug(LoggingLevel.INFO, '>>> result: ' + result);

        boolean hasExportPermissions = false;
        for (AggregateResult ar : result)  {
            System.debug('>>> Count: ' + ar.get('expr0'));
             if (ar.get('expr0') != 0) {
              hasExportpermissions = true;  
             }
        }

        System.debug(LoggingLevel.INFO, '>>> hasExportPermissions: ' + hasExportPermissions);        
        
        // Take policy action only if the user profile is not 'System Administrator' 
        // AND user does not have 'Export_Reports' permission set 
        // AND RowsProcessed greater than 250.
        if (!profile.Name.contains('System Administrator')
            && !hasExportpermissions
            && ReportEvent.Operation.equals ('ReportExported')
            && reportEvent.RowsProcessed > 250) {
            return true;
        }
        return false;
    }
}

You must log in to answer this question.

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