Row Level Security with SQL Server 2016: Part 1 – Allow Access to Only a Subset of Rows Using Row Level Security
Ever wanted an easier way to provide security at the record level? If so, now you can easily do this with the Row Level Security feature that is being introduced with SQL Server 2016.
Row Level Security (RLS) is a concept that provides security at the row level within the database layer, instead of at the application layer. RLS is accomplished by using a function and the new security policy feature that is being rolled out with SQL Server 2016. With this new feature you can implement RLS without even changing your application code.
In order to enforce RLS on a SQL Server table SQL you need to be able to build an inline table value function that will restrict rows based on a WHERE predicate. Typically that WHERE predicates is based off the database user associated with the SQL Server command being executed. When the inline table value function is associated with a security policy the SQL Server engine is able to filter out rows based on the results of the function.
Let us define a business problem, and then resolve the problem by implementing RLS using the new CREATE SECURITY POLICY feature that is co define a business problem, and then resolve the problem by implementing RLS using the new CREATE SECURITY POLICY feature that is coming out with SQL Server 2016.
Suppose you currently allow sales people to view the customer account information for your company without any row level restriction. The table that contains customer account information looks like this:
CREATE TABLE [dbo].[customer] |
Currently the Sales people browse the customer information by running the following SELECT statement:
SELECT customername, |
Management has realized that some sales people have not only been looking at their individual customer records, but they have also been looking at other sales person’s customers, in hopes of stealing those customers away from the current sales person. Therefore I’ve have been asked to make sure when sales people run the SELECT statement above, the command will only return the customer information for records where the SalesPersonUserName is equal to the login for the salesperson.
Customer Data
For testing purposes the following script generates a database, database users, my dbo.Customer table, and some sample data that will use for testing the SQL Server 2016 Row Level Security feature:
USE master;goCREATE DATABASE rls_demo; |
Solution to Business Problem
To build a solution so each salesperson can only see their customers I will be utilizing a new SQL Server 2016 feature known as Record Level Security (RLS). In order to build record level security requirement for dbo.Customer table we will be creating a function and a security policy.
Function will be used to filter out rows based on the database user. Each sales person logs on to SQL Server with their own SQL account, which means each sales person has a different database user in my RLS_Demo database. Therefore my function is able to use the user_name function to drive the filtering process for my RLS example. Below is the code that will use to create filtering predicate function:
CREATE FUNCTION Fn_rowlevelsecurity (@FilterColumnName SYSNAME) |
The function code above accepts a parameter named @FilterColumnName, and then uses this parameter to filter out the rows based on the database user, using the user_name function. You might be asking yourself how this function will be used to filter rows from customer table.It will do that by associating this function with a security policy that will create by running the following code:
CREATE security policy filtercustomer ADD filter predicate dbo.fn_rowlevelsecurity(salespersonusername) ON dbo.customer WITH (state = ON); |
In the security policy defined above can see we added a FILTER PREDICATE that referenced my dbo.fn_RowLevelSecurity function. By creating this security policy the SQL Server engine will make sure every time that a database user runs a SQL command that references the dbo.Customer table that the filter predicate dbo.fn_SecureCustomerData function will also be executed, thus enforcing the RLS rules I defined in the filter.
To see this in action let us run the following code to test new RLS setup in my RLS_DEMO database for all three of my different databases users (Jane, Dick and Sally) that have accesses to the dbo.Customer table in my RLS_DEMO database. To run my demo will run the following code:
EXECUTE AS USER = 'Jane'; PRINT 'Jane''s Customers'; |
When run this code ,get the following output:
Jane's Customers
CustomerName CustomerEmail SalesPersonUserName ------------------------------ ------------------------ -------------------- ABC Company Manger@ABC.COM Jane Info Services info@AInfaSerice.COM Jane Star Brite steve@starbright.COM Jane
Dick's Customers
CustomerName CustomerEmail SalesPersonUserName ------------------------------ ------------------------ -------------------- Washing-R-Us HeadWasher@washrus.COM Dick Blue Water Utilities marketing@bluewater.COM Dick
Sally's Customers
CustomerName CustomerEmail SalesPersonUserName ------------------------------ ------------------------ -------------------- Rainy Day Fund Tom@rainydayfund Sally
As you can see, by looking at this output, the SELECT statement that was run when the database user was set to Jane returned 3 rows and each row had the value ‘Jane’ in the SalesPersonUserName column. The user Dick returned just 2 rows and Sally only had one Customer record associated with her user name.
If you review the predicate function dbo.fn_RowLevelSecurity that created above you would see that that function didn’t reference my dbo.Customer. This function was able to restricted rows on the dbo.Customertable, because the security policy FilterCustomer referenced that predicate function when I created this policy.
Since predicate filter logic didn’t filter on a specific table I can reuse that filter to restrict row level access from other tables, provided they require the same filter logic. To demonstrate this create and populate a new table then associate new table with same dbo.fn_RowLevelSecurity filter predicate functionusing the following code:
CREATE TABLE supplier |
Now that we have created this new security policy named FilterSupplier let me verify that my Supplier table will be filtering rows using the same predict function that I used to filter the dbo.Customer table by running the following code. Run this code yourself to verify that my new security policy FilterSupplier does in fact filter out rows correctly when each database user tries to select rows from the dbo.Supplier table:
EXECUTE AS USER = 'Jane';PRINT 'Jane''s Suppliers'; |
Limitations
As with any new feature there are a number of limitation. Here are a few of those limitations:
As you can see, implementation of row level security can be done easily and without changing the existing application. This new feature will allow you to add record level security rules in your database design. If you need to provide record level security then you should consider rolling out those record level security predicates as soon as you have migrated or implemented your application in a SQL Server 2016 environment.
Years of Experience
Gratified Students
Training Batches
Training Hours
Please subscribe our technical blog to get recent updates.