Wave Security Predicates using Custom Permissions

In Wave, row-level security is enforced using predicates, and it works a bit like this:

<dataset column> <operation> <value>

You can go for simple comparisons or get sophisticated and apply security at a record owner, role-based or even team-based level.  For the skinny on that, see this post:

http://explore.wave6.com/blog/how-security-is-applied-to-dashboards-of-salesforce-analytics-cloud

But… once you go down that road, it can get tricky to open up rows so that, for example, System Administrators can see all records. One common approach is to create a formula field on the target object that always evaluates to ‘View All’, a field on the User record that can be set manually and add to the security predicate ‘ViewAllField__c == $User.ViewAllField__c’. If you set the field to ‘View All’ on a user’s record, voila, they can see all records.

Let’s see if we can streamline and add some flexibility by using security features we already have available via custom permissions applied per profile. If you’re not familiar with custom permissions, custom permissions let you define access checks that can be assigned to users via permission sets or profiles, similar to how you assign user permissions and other access settings.

Step 1: Create a custom permission

  1. Navigate to Setup | Develop | Custom Permissions.
  2. Click New and set the label to ‘View All Analytics Data’ and the name to  ‘View_All_Analytics_Data’ and hit Save. Note that you could be put any name in as long as it matches Step 3.

 

Step 2: Assign that Custom Permission to one or more profiles

  1. Navigate to Setup | Manage Users | Profiles.
  2. Select System Administrator (or any other profile you prefer).
  3. Select Custom Permissions and enable ‘View All Analytics Data’ custom permission.

 

Step 3: Create a formula field on object that always evaluates to the Custom Permission Name.

  1. Navigate to Customize | Opportunities | Fields and click New.
  2. Set as Formula and then Text.
  3. Set Label as ‘Permission View All Analytics Data’ and Name ‘Permission_View_All_Analytics_Data’.

Note that while in this case we call it ‘Permission_View_All_Analytics_Data’, you could name it anything as long as it outputs to the developername of the custom permission you are targeting.

 

Step 4: Update Dataflow with this JSON

  1. Copy and paste the following JSON into a file locally on your computer.
  2. Navigate to Data Monitor.
  3. Click Upload and select the file you saved.
  4. Navigate to Data Monitor and Start the Dataflow

Don’t be wary, this a lot but most will be copy and paste. Note if you already have an existing dataflow, you will want to Download from Data Monitor and append this JSON to it before uploading.

{
  "Digest_Profile": {
    "action": "sfdcDigest",
    "parameters": {
      "object": "Profile",
      "fields": [
        {
          "name": "Id"
        },
        {
          "name": "Name"
        }
      ]
    }
  },
  "Digest_PermissionSet": {
    "action": "sfdcDigest",
    "parameters": {
      "object": "PermissionSet",
      "fields": [
        {
          "name": "Id"
        },
        {
          "name": "ProfileId"
        },
        {
          "name": "Name"
        }
      ]
    }
  },
  "Digest_SetupEntityAccess": {
    "action": "sfdcDigest",
    "parameters": {
      "object": "SetupEntityAccess",
      "fields": [
        {
          "name": "SetupEntityType"
        },
        {
          "name": "Id"
        },
        {
          "name": "SystemModstamp"
        },
        {
          "name": "ParentId"
        },
        {
          "name": "SetupEntityId"
        }
      ]
    }
  },
  "Digest_CustomPermission": {
    "action": "sfdcDigest",
    "parameters": {
      "object": "CustomPermission",
      "fields": [
        {
          "name": "CreatedDate"
        },
        {
          "name": "Id"
        },
        {
          "name": "IsDeleted"
        },
        {
          "name": "Description"
        },
        {
          "name": "MasterLabel"
        },
        {
          "name": "LastModifiedDate"
        },
        {
          "name": "Language"
        },
        {
          "name": "DeveloperName"
        },
        {
          "name": "NamespacePrefix"
        },
        {
          "name": "SystemModstamp"
        }
      ]
    }
  },
  "Digest_Opportunity": {
    "action": "sfdcDigest",
    "parameters": {
      "object": "Opportunity",
      "fields": [
        {
          "name": "Id"
        },
        {
          "name": "Permission_View_All_Analytics_Data__c"
        }
      ]
    }
  },

  "Augment_SetupEntityAccess_CustomPermission": {
    "action": "augment",
    "parameters": {
      "relationship": "CustomPermission",
      "left_key": [
        "SetupEntityId"
      ],
      "right_key": [
        "Id"
      ],
      "left": "Digest_SetupEntityAccess",
      "right": "Digest_CustomPermission",
      "right_select": [
        "CreatedDate",
        "Id",
        "IsDeleted",
        "Description",
        "MasterLabel",
        "LastModifiedDate",
        "Language",
        "DeveloperName",
        "NamespacePrefix",
        "SystemModstamp"
      ]
    }
  },
  
  "Augment_SetupEntityAccess_PermissionSet": {
    "action": "augment",
    "parameters": {
      "relationship": "PermissionSet",
      "left_key": [
        "ParentId"
      ],
      "right_key": [
        "Id"
      ],
      "left": "Augment_SetupEntityAccess_CustomPermission",
      "right": "Digest_PermissionSet",
      "right_select": [
        "Id",
        "ProfileId",
        "Name"
      ]
    }
  },

  "Augment_SetupEntityAccess_PermissionSet_Profile": {
    "action": "augment",
    "parameters": {
      "relationship": "PermissionSet.ProfileId",
      "left_key": [
        "PermissionSet.ProfileId"
      ],
      "right_key": [
        "Id"
      ],
      "left": "Augment_SetupEntityAccess_PermissionSet",
      "right": "Digest_PermissionSet",
      "right_select": [
        "Id",
        "Name"
      ]
    }
  },
  
  "Augment_Opportunity_View_All_Profiles": {
    "action": "augment",
    "parameters": {
      "relationship": "SetupEntityAccess.ViewAllAnalyticsData",
      "operation": "LookupMultiValue",
      "left_key": [
        "Permission_View_All_Analytics_Data__c"
      ],
      "right_key": [
        "CustomPermission.DeveloperName"
      ],
      "left": "Digest_Opportunity",
      "right": "Augment_SetupEntityAccess_PermissionSet_Profile",
      "right_select": [
        "PermissionSet.ProfileId"
      ]
    }
  },
  
  "Register_Opportunity": {
    "action": "sfdcRegister",
    "parameters": {
      "source": "Augment_Opportunity_View_All_Profiles",
      "alias": "OppsViewAllProfiles",
      "name": "OppsViewAllProfiles",
      "rowLevelSecurityFilter": "'SetupEntityAccess.ViewAllAnalyticsData.PermissionSet.ProfileId' == \"$User.ProfileId\""
    }
  }
}


This dataflow digests all records in SetupEntityAccess, CustomPermission, PermissionSet and Profiles like such:

The real magic, though, is the step Augment_Opportunity_View_All_Profiles. It matches the word written in Opportunity.Permission_View_All_Analytics_Data__c to any Custom Permission with the same developername. It then returns any profiles associated to the custom permission and stores them in the column named SetupEntityAccess.ViewAllAnalyticsData.PermissionSet.ProfileId.

To control visibility, the Register_Opportunity step’s rowLevelSecurityFilter parameter is set to ‘SetupEntityAccess.ViewAllAnalyticsData.PermissionSet.ProfileId’ == “$User.ProfileId”. This checks if the current running user’s profile is in the set of acceptable profiles to view each row as determined by the Augment_Opportunity_View_All_Profiles step.

After the Dataflow finishes, voila (again)! A dataset called OppViewAllProfiles will be created, and any user with a profile assigned the View_All_Analytics_Data custom permission will see every opportunity row in this dataset.

Conclusion

Of course, it doesn’t have to stop here. We could have another field on Opportunity that evaluates to different custom permission names based on, say, business unit. There we could grant users access to not all but a subset of records. And who knows… possibly you’ll find other novel applications using this technique.

In an upcoming blog post, we will demonstrate how to add even more flexibility by creating security predicates based only on permission sets.

Published
August 28, 2015

Leave your comments...

Wave Security Predicates using Custom Permissions