If you’ve taken Salesforce Admin 201 training, you might remember learning about Sharing Settings. Sharing Settings include Sharing Models, Criteria Sharing Rules, Manual Sharing, and more. I’m a software engineer on the Record Access Experience team here at Salesforce. When I took this training in 2017, it was the first time Sharing Settings truly clicked for me, even though I’d been hired onto the team a month earlier. In this post, I’m going to talk about Sharing, then describe a new feature called Restriction Rules and how it’s implemented differently from other Sharing Settings.
How Sharing Settings give you access to a record is described in this image.
If a user gets access to a record because your Org’s Org-Wide-Default gives them access, Salesforce doesn’t check anything else. If not, we check to find out if your Role, Owner or Criteria Sharing Rules, or Manual Shares give you access. If none of these settings grant access, we return a “You don’t have access” message.
Under the Hood: Sharing
Sharing works by materializing Share Rows in a table. For instance, if Maria owns an account, then we insert a row in an Account Sharing Table. The new row indicates that Maria can access the account because she’s the owner.
If a Sharing Rule grants access to another user, Frank, we insert a row in the Account Sharing Table. The new row indicates that Frank has access because it was granted manually. If a Sharing Rule grants access to a group called “Strategy,” then the row in the Account Sharing Table points to the group.
Now, if another user pulls this account up, we look at the Account Sharing Table to determine if they have direct access to it. In other words, we check if they are Maria or Frank, who have access because they are the owner or gained access via Sharing Rule. Salesforce also performs a lookup in multiple Group Maintenance Tables to determine if the user is part of the Strategy group.
We have indexes and highly optimized queries on these tables, which typically gives us our lightning-fast performance at the point of accessing data. As you can see, we get a speedy response because we make a trade on the space side of the space-time trade-off. We write a whole bunch of rows in our tables to describe who sees what. While it may be fast at enforcement time, when rules are created, updated, or deleted, we end up having to rewrite a whole host of records. That process can be time-consuming. In most cases, writing these rows happens quickly. But there are times when this process increases wait times, and customers are prevented from updating object access.
The Restriction Rules feature is the newest addition to record-level access control mechanisms at Salesforce. For a while now, Sharing Settings have needed enhancement. There are objects that should have their own sharing model, but don’t. There are objects that have their own distinct access control mechanisms. And there are objects that observe Sharing Settings, but could use improvement. Restriction Rules aim to plug these gaps and position Salesforce to make future forays into uncharted territory, such as data that resides outside of Salesforce, or data that is not stored in a relational DB. If you have other ideas, let us know in the comments.
Setting Up a Restriction Rule
A Restriction Rule might look something like this.
This is a sample Restriction Rule created on a task object. Check out the User Criteria and Record Criteria sections. This rule states that users in the US can only access tasks that they created. So even if the users get access to tasks that they didn’t create through an existing access control mechanism, Restriction Rules act as a filter that is applied on top of that mechanism. The Restriction Rules thus deny access to those tasks.
Now, if we updated our Sharing triangle, it would look like this.
Under the Hood: Restriction Rules
Restriction Rules invert the space-time tradeoff. Rather than store access in the Sharing Tables, we thought, why not generate an Access Control Policy that could be read at runtime to define access? This massively reduces the time it takes to create, update, or delete rules because we no longer have to materialize all these records. Furthermore, at the point of enforcement, we can reduce the number of times we make a database round trip to look at all the Sharing and Group Maintenance Tables.
eXtensible Access Control Markup Language (XACML)
Enter XACML (and a few acronyms). Wikipedia says it’s a declarative Attribute-Based Access Control (ABAC) policy language. It’s a published standard specification that aims to function as an access control system. This system uses attributes associated with a user, action, or resource as inputs into the decision of whether a given user may access a given resource in a particular way. Perfect! Let’s have our users create and upload these policies. Well, not so fast. XACML policies are powerful, but they can be verbose and can get complicated quickly as seen in this example from the same Wikipedia article.
Rather than make our users learn a new language altogether, we decided to make it simple to author rules either through the UI (as seen above) or the Metadata API.
In this image, if an admin user creates a Restriction Rule, we generate a XACML policy and store that in a XACML data store. Then, when a user accesses a record, before we go retrieve the data, we make a request to a Policy Decision Engine. This makes a decision based on what the policy says. That decision informs whether or not the user has access.
In fact, it’s not that easy. There’s more happening here than meets the eye.
- To make the request to the Policy Decision Engine, we need to know which pieces of data we need to supply to it. For instance, if a Restriction Rule on a Task was created as in our example, then the Decision Engine needs to know the current user’s country in order to compare it with the country named in the rule. Similarly, it needs to know the record’s creator. We solve this by scanning the policy once it’s created, then caching a catalog of attributes that the policy references. Then, when a user makes a request for one or more records, we look at the catalog, and supply that information to the Decision Engine along with the request.
- If a user is making a query for multiple records, we cannot afford to iterate through all of them and ask the Decision Engine for a decision on each record. In such situations, we make just one call to the Decision Engine, which will then return a filter. Whilst querying for the data, we add that filter to the query, thereby eliminating records the user does not have access to.
Performance Performance Performance!
Every time we write a new line of code, we incur a cost. When it comes to performance, the best feature is one that is never written. Of course, we wrote the feature, but worked to aggressively reduce overhead. A feature such as Restriction Rules, which could potentially be run every time a user pulls up a piece of data, must be as performant as possible. Our early iterations, much to our performance engineering team’s chagrin, were quite chatty with the database. This led to increased CPU and DB usage and response times.
We formulated a two-pronged approach to tackle our performance issues.
- Eliminate or tune SQL queries: Some queries could be eliminated. We could infer those results from data we already had cached in memory. We rewrote some others to be more performant.
- Caching: We aggressively cached data that would get frequently retrieved. Initially we cached things like the catalog of attributes and the contents of the XACML policy, thereby reducing the database round trips to retrieve the policy. On further examination, it became obvious that we’d get significant benefits by remembering the decision itself, as long as nothing else had changed. So we wrote a mechanism to reuse the decision.
After these performance improvements, preliminary testing revealed that, on average, the overhead of adding Restriction Rules in terms of response times decreased by almost 80%.
An Updated Architecture Diagram
Factoring in the above changes makes our architecture look like this.
Restriction Rules are a powerful new feature that enhance our existing access control mechanisms. Furthermore, the underlying architecture that went into creating this feature also gives our engineering team a platform to create newer and more flexible access control mechanisms in the future. We invite you to try this feature out for yourself. Then, leave us some feedback!
- Learn MOAR in Winter ’22 with Restriction Rules 🚫
- Control Access to Sensitive Records with Restriction Rules
Thank you for reading! This project would not have been possible without the tireless efforts of the following people: Yanik Grignon, Matthew Wong, Larry Tung, Devshree Gehlot, Bhumi Damania, Priyank Bhawsar, Subhash Uppalapati, Benjamin Gee, Shyam Naren Kandala, Sunghyun Song, Virag Shah, Irina Sendyk, Arjun Sirohi, Lakshman Martha, Dan Sheehan, Dana Holloway, Alex Kaufman, Yasoja Seneviratne, Chuxi Wang, Chidambaram Kadiresan, Andy Kuang, and many others.