PolicyCondition Interface

Apex interface that allows an implementing class to specify actions to take when certain events occur based on a transaction security policy.

Namespace

TxnSecurity

Usage

The evaluate method is called upon the occurrence of an event monitored by a transaction security policy. A typical implementation first selects the item of interest from the event. Then the item is tested to see if it meets the condition being monitored. If the condition is met, the method returns true.

For example, imagine a transaction security policy that checks for the same user logging in more than once. For each login event, the method would check if the user logging in already has a login session in progress, and if so, true is returned.

If you’re using the policy condition interface in the org where the policy was implemented, test classes for the policy are not required. If you move the policy to another org, you must have test classes for the Apex policy in the new org. Testing is required whether the policy is moved from a sandbox to production, with a change set, or some other way. Why? If you’re making a policy available outside of its development environment, it needs testing to make sure it works correctly.

To avoid errors, don't include DML statements in your custom policies. Also, if you send custom emails via Apex during transaction policy evaluation, you’ll get an error when the policy is evaluated, even if the record is not explicitly related to another record. For more information, see Apex DML Operations in the Apex Developer Guide.

PolicyCondition Methods

The following is the method for PolicyCondition.

  • evaluate(event)
    Evaluates an event against a transaction security policy. If the event triggers the policy, true is returned.

evaluate(event)

Evaluates an event against a transaction security policy. If the event triggers the policy, true is returned.

Signature

public Boolean evaluate(TxnSecurity.Event event)

Parameters

event
Type: TxnSecurity.Event
The event to check against the transaction security policy.

Return Value

Type: Boolean

When the policy is triggered, True is returned. For example, let’s suppose the policy is to limit users to a single login session. If anyone tries to log in a second time, the policy’s action requires that they end their current session. The policy also sends an email notification to the Salesforce admin. The evaluate() method only checks the login event, and returns True if it’s the user’s second login. The Transaction Security system performs the action and notification, and not the evaluate() method.

PolicyCondition Example Implementations

Here are a variety of code samples to show how to implement the TxnSecurity.PolicyCondition class for Transaction Security. These examples show how to use different event components to identify and check a wide variety of condition.

PolicyCondition Example: Block Localhost Login

How to use the IP address in a login policy. This example implements a policy that triggers when there’s a login from localhost.

First, on the Transaction Security setup page, create a policy to block localhost logins:New Custom Transaction Security Policy page to set up Block Localhost Login policy

The following example is the generated Apex policy. Indenting and comments have been added to improve legibility.

global class BlockLocalhostLoginPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    // Get the LoginHistoryId to in turn select the SourceIp address.
    String loginHistoryId = e.data.get('LoginHistoryId');
    // Retrieve SourceIp from LoginHistory.
    LoginHistory eObj = 
      [SELECT SourceIp FROM LoginHistory WHERE id = :e.data.get('LoginHistoryId')];
    // If the Source IP is localhost (127.0.0.1), trigger the policy and return true.
    if(eObj.SourceIp == '127.0.0.1') {
      return true;
    }
    return false;
  }
}

PolicyCondition Example: Block Large Data Export

How to check for large data transfers in a login policy. This example implements a policy that triggers when 2,000 records or more are downloaded via the API.

An admin or other customer with API privileges can download all customer data in bulk using SOAP API, REST API, or the Bulk API. This security policy restricts API-based data downloads to 2,000 records and alerts the admin with a real-time notification if the policy is triggered.

global class DataLoaderExportPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    Boolean isApi = Boolean.valueOf(e.data.get('IsApi')) { // For any API request...
    Integer numberOfRecords = Integer.valueOf(e.data.get('NumberOfRecords'));
      if (isApi && numberOfRecords >= 2000) {
        return true;
      }
      return false;
    }
  }
}

PolicyCondition Example: High-Assurance Session

How to require a high-assurance login session when accessing confidential data. This example implements a policy that requires everyone to use two-factor authentication before they can access a specific report.

You can have sensitive, confidential data in your quarterly Salesforce reports. You also want to ensure that teams accessing those reports use two-factor authentication (2FA) for high assurance before viewing this data. The policy makes 2FA a requirement, but you can’t provide high-assurance sessions until your teams have a way to meet the 2FA requirements. As a prerequisite, first set up 2FA in your Salesforce environment.

This example highlights the capability of a policy to enforce 2FA for a specific report. The report defined here is any report with “Quarterly Report” in its name. Anyone accessing the report is required to have a high-assurance session using 2FA.

global class ConfidentialDataPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    if (e.resourceType == 'Dashboard') {     // If the event is about Dashboards...
      Dashboard dashboard =
        [SELECT DeveloperName FROM Dashboard WHERE id = :e.entityId];
      String name = String.valueOf(dashboard.DeveloperName);
      // Check if this is a quarterly report.
      if (name.containsIgnoreCase('Quarterly Report')) {
        return true;
      }
    }
    return false;
  }
}

PolicyCondition Example: Restricting Platform Browser

How to check for a specific operating system and browser in a login policy. This example policy triggers when a user with a known OS and browser combination tries to log in with any other browser on a different OS.

Here’s a policy example for restricting access. Many organizations have standard hardware and support specific versions of different browsers. You can use this standard to reduce the security risk for high impact individuals by acting when logins take place from unusual devices. For example, your CEO typically logs in from San Francisco using a Macbook or Salesforce mobile application on an iPhone to Salesforce. When a login occurs from elsewhere using a Chromebook, it’s highly suspicious. Since hackers do not necessarily know which platforms corporate executives use, this policy makes a security breach less likely.

In this example, the customer organization knows that their CEO is using a Macbook running OS X with the Safari browser. Any attempt to log in using the CEO’s credentials with anything else is automatically blocked.

global class CeoBrowserAccessPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    // If it's a Login attempt from our CEO's user account.
    if (e.action == 'Login' && e.userId == '005x0000005VmCu') {
      // Get the platform & browser from LoginHistory for this login attempt.
      LoginHistory loginAttempt =
        [SELECT Platform, Browser FROM LoginHistory
         WHERE Id = :e.data.get('LoginHistoryId')];
      String platform = loginAttempt.Platform;
      String browser = loginAttempt.Browser;
      // The policy is triggered when the CEO isn’t using Safari on Mac OSX.
      if (!platform.equals('Mac OSX') || !browser.startsWith('Safari')) {
          return true;
      }
    }
    return false;
  }
}

PolicyCondition Example: Block Access by Geography

How to block access completely for logins from a specific area. This example implements a policy that blocks access by country.

Your organization could have remote offices and a global presence but, due to international law, wants to restrict access to their Salesforce org. The restrictions would be from specific countries, or obtain alerts when unusual login activity occurs.

This example builds a policy that blocks users logging in from North Korea. If users are in North Korea but using a corporate VPN, their VPN gateway would be in Singapore or the United States. The VPN gateway would make their login successful because Salesforce would see the internal US-based company IP address.

global class BlockAccessFromNKPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    // Get the login history.
    LoginHistory loginAttempt =
      [SELECT LoginGeoId FROM LoginHistory WHERE Id = :e.data.get('LoginHistoryId')];
    // Get the login's geographical info.
    String loginGeoId = String.valueOf(loginAttempt.LoginGeoId);
    LoginGeo loginGeo = [SELECT Country FROM LoginGeo WHERE Id = :loginGeoId];
    // Get the country at that location.
    String country = String.valueOf(loginGeo.Country);
    // Trigger policy and block access for any user trying to log in from North Korea.
    if(country.equals('North Korea')) {
        return true;
    }
    return false;
  }
}

You can also restrict access to other specific values, like postal code or city.

PolicyCondition Example: Block Access by OS

How to block access for anyone using a specific operating system. This example implements a policy that blocks access for anyone using an older version of the Android OS.

You’re concerned with a specific mobile platform’s vulnerabilities and its ability to capture screen shots and read data while accessing Salesforce. If the device is not running a security client, you could restrict access from device platforms using operating systems with known and well-identified vulnerabilities. In this example, we create a policy to block devices using Android 5.0 or earlier.

global class BlockOldAndroidDevicesPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    LoginHistory loginAttempt =
      [SELECT Platform FROM LoginHistory WHERE Id = :e.data.get('LoginHistoryId')];
    if (loginAttempt != null) {
      String platform = loginHistory.Platform;
      if (platform.contains('Android') && platform.compareTo('Android 5') < 0) {
        return true;
      }
    }
    return false; // Allow access from Android versions greater than 5.
  }
}

PolicyCondition Example: Block Specific Words in Chatter Posts

How to block Chatter posts containing certain words or content. This example implements a policy that blocks a specified word by searching the Chatter post’s text.

You can scan or filter for specific words in posts. This example looks for a post containing the specific word “Salesforce” and blocks those posts. You can also write conditions that loop through a list of words or keep a running total of the occurrence of the words.

global class ChatterMessageWordFilterPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event event) {
    String body = event.data.get('Body');

    if(body.containsIgnoreCase('Salesforce')) {
      return true;
    }
    return false;
  }
}
Note

Note

If you’re comparing the contents of the entire post, don’t use the equals string method—use contains, or containsIgnoreCase as shown here. If the Chatter settings are on to show emoticons or to allow rich text, those items are included in the Chatter post’s body. For example, with rich text, the post “This is text.” could be stored as “<p>This is text.</p>”. If you use the equals method, the embedded tags prevent your otherwise identical comparison text from matching the post body.

PolicyCondition Example: Using Apex API Callouts

How to block Chatter posts with certain words or content. This example implements a policy that blocks profanity by using an external service.

Advertisers and spammers often post messages to successful communities at high rates to increase their chances of people clicking their links. The links can include unwanted content. You can use technologies outside of Salesforce to scan or filter content based on these different services.

In this example, we have unwanted text in communities posts and the policy executes an API callout to see if the content is compliant. This example uses a service that blocks commonly-accepted English profanity as specified at www.purgomalum.com/profanitylist.

global class ChatterMessageProfanityFilterPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    String body = e.data.get('Body');

    //Create HTTPRequest and specify its type and properties.
    HttpRequest request = new HttpRequest();
    request.setMethod('GET');
    request.setHeader('content-type', 'text/plain');
    request.setHeader('Connection', 'keep-alive');
    request.setEndpoint('http://www.purgomalum.com/service/containsprofanity?text=' +
                        EncodingUtil.urlEncode(body,'UTF-8'));

    Http http = new Http();
    HTTPResponse response = http.send(request);

    if (response.getStatusCode() == 200 && response.getBody().equals('true')) {
      return true; // Callout succeeded and found profanity in the message.
    }
    return false;  // Callout failed or no profanity was found.
  }
}
Note

Note

If an API callout’s elapsed execution time exceeds 3 seconds, the user is denied access to the resource or entity. For more information, see Transaction Security Metering.

PolicyCondition Example: Block Connected App Access

Block a Connected App with API access from accessing large amounts data.

Sometimes connected apps have API privileges to access data org-wide due to sharing or account access settings definitions. However, the end user of the connected app is restricted to only a specific data set. This conflict can result in an increased security risk by identifying the API key and performing command-line searches directly in the database to look for leads. The following policy avoids this situation and data loss around your company’s lead information.

global class DataLoaderLeadExportPolicyCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    if (Boolean.valueOf(e.data.get('IsApi'))) {

      // The event data is a Map<String, String>. We need to call the 
      // valueOf() method on appropriate data types to use them here.
      String resourceType         = e.data.get('resourceType');
      String connectedAppId       = e.data.get('ConnectedAppId');
      Integer numberOfRecords     = Integer.valueOf(e.data.get('NumberOfRecords'));
      Integer executionTimeMillis = Integer.valueOf(e.data.get('ExecutionTime'));

      // We're looking for leads accessed by a specific connected app that is
      // transferring more than 2,000 records a second - a large transfer.
      if ('Lead'.equals(resourceType) &&
        '0CiD00000004Cce'.equals(connectedAppId) &&
        numberOfRecords > 2000 &&
        executionTimeMillis > 1000) {
          return true;
        }
    }
    return false;
  }
}