Often I have come across situations where the enterprise customers want to restrict data availability to their employees / IT not only from the application but also at the database level. Government / Public sectors have a common requirement that they don’t want everybody at their IT team to be able to query everybody’s data.
As of today there is no out of the box support for this feature in on-premise SQL. The implementation we are going to talk about is not something new but a pretty commonly used logic in Dynamics CRM. I call it “filtered views”.
Most of the SQL developer / DBA would agree to the fact that never expose a table directly to a Stored Procedure / application / dev instead, create views. The idea is similar here. We will use views to filter out the records the user / dev is seeing.
The row filtering logic is governed by the requirement and therefore there is no right or wrong way of implementation.
Row filtering based on user roles.
The approach here is that rows will be filtered based on the SQL Server Roles the end-user has.
Scenario:
In AdventureWorks2012 database I created 2 database roles created “Developers” and “Administrators”. Developers can see the “EmployeePayHistory” for the employees who are of level 3 and greater and Administrators can see everybody’s. Below is how I would write the view for this.
CREATE
FUNCTION GetPermissibleLevel()
RETURNS
INT
AS
BEGIN
DECLARE @minLevel INT
IF IS_RoleMember(‘Administrators’) = 1
SET @minLevel = 0
ELSE
SET @minLevel = 3
RETURN @minLevel
END;
GO
CREATE
VIEW vEmployeePayHistory
AS
SELECT HumanResources.EmployeePayHistory.*
FROM HumanResources.EmployeePayHistory
INNER
JOIN HumanResources.Employee ON HumanResources.Employee.OrganizationLevel >=
(SELECT [dbo].GetPermissibleLevel())
Row filtering based on CLR function
Replace the above function with a CLR function and that’s it. So where can you put them into use?
- If you had custom business logic for authorization of the user, then a CLR function can leverage and re-use this.
- If you had a ADFS / ACS / even Azure based authorization logic, those can be ported here.
With great power, comes greater responsibility; therefore ensure that whatever you write in that logic returns the result within a micro second or else you will be introducing performance nightmare for every single SQL call. Ensure appropriate caching is in place for every reused authorization data.
Have you taken a look at the built-in Row-Level Security feature in SQL Server 2016 and Azure SQL Database? You may find that it performs better (predicates are inlined) and is easier to manage & maintain (logic is centralized in a security policy) than workarounds using views.
Books Online: https://msdn.microsoft.com/library/dn765131.aspx
Yes Tommy, I am very well aware of that and that’s why I have mentioned “on-premise” 🙂
Have you taken a look at the built-in Row-Level Security feature in SQL Server 2016 and Azure SQL Database? You may find that it performs better (predicates are inlined) and is easier to manage & maintain (logic is centralized in a security policy) than workarounds using views.
Books Online: https://msdn.microsoft.com/library/dn765131.aspx
Yes Tommy, I am very well aware of that and that’s why I have mentioned “on-premise” 🙂
Yes Tommy, I am very well aware of that and that’s why I have mentioned “on-premise” 🙂