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.

Don’t include Data Manipulation Language (DML) statements in your custom policies. DML operations are rolled back after a transaction security policy is evaluated, regardless if the policy evaluates to true or false.

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.

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.

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.
global class BlockLocalhostCondition implements TxnSecurity.PolicyCondition {
  public boolean evaluate(TxnSecurity.Event e) {
    // Get the LoginHistoryId to in turn get the SourceIp address.
    String loginHistoryId = e.data.get('LoginHistoryId');
    // Retrieve SourceIp from LoginHistory.
    LoginHistory loginAttempt = 
      [SELECT SourceIp FROM LoginHistory WHERE id = :loginHistoryId];
    String sourceIp = String.valueOf(loginAttempt.SourceIp);
    // If it's localhost, the policy is triggered and true is returned.
    if (sourceIp != null && sourceIp.equals('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 Salesforce1 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: 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.
  }
}

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;
  }
}