Flexible Database-Level Roles
This is following up from my previous post on Database Roles. In my previous post, I talked about fixed database roles. If you’ve forgotten, go back and re-read the article. I’ll still be here when you get back.
So, what exactly are flexible database roles? They are roles that you create in the database. It’s as simple as that.
When you start with a new database, there are no flexible roles, just the fixed roles. The upside of this is that you are free to create all of the roles that you need and grant all of the permissions that you desire to these roles.
So, where to begin? Let’s start with an empty database:
-- Create our sample database and switch to it CREATE DATABASE TestFixedRoles; GO USE TestFixedRoles; GO -- Create a new user and grant them db_securityadmin CREATE USER user_security WITHOUT LOGIN; CREATE USER user_test WITHOUT LOGIN; GO EXEC sp_addrolemember N'db_securityadmin', N'user_security'; GO
So, now we’ve created a new database, a new user, and granted db_securityadmin to our new user. You might remember from the previous article that db_securityadmin can’t grant access to fixed database roles. However, db_securityadmin has no such limitation when dealing with flexible database roles.
Now, let’s set up a test user for the purposes of this introduction to flexible database roles.
CREATE USER test_user WITHOUT LOGIN; GO
This user is now a member of the PUBLIC role. What kind of access does public have?
CREATE SCHEMA test; GO CREATE TABLE test.t1 (number INT); GO DECLARE @i INT; SET @i = 0; WHILE @i < 1000 BEGIN INSERT INTO test.t1 VALUES (@i); SET @i = @i + 1; END EXECUTE AS USER='test_user'; GO -- This will fail because we have no access to the test schema SELECT * FROM t1; GO REVERT GO
None. PUBLIC has no access, thus test_user has no access. Let’s get test_user set up with some access.
CREATE ROLE test_role; GO -- The scope qualifier '::' is required. GRANT SELECT ON SCHEMA :: test TO test_role; GO EXEC sp_addrolemember N'test_role', N'test_user'; GO EXECUTE AS USER='test_user'; GO -- Success! SELECT * FROM t1; GO REVERT GO
There you have it! We can select from the test schema. How does this help, though, in the real world?
For starters, by creating roles and adding users to roles you can streamline managing security through the use of roles rather than having to monitor the permissions assigned to every user, login, Windows user, and Windows group.
Second, by combining roles for managing security with schemas and stored procedures, it’s possible to carefully control and define granular access to stored procedures and data.
Let’s take a look at AdventureWorks and create an example of how we might want to accomplish this.
This procedure just retrieves customers who placed an order between two dates. Pretty simple.
CREATE PROCEDURE Sales.GetCustomersWithOrdersBetweenDates ( @StartDate DATETIME, @EndDate DATETIME ) WITH EXECUTE AS OWNER AS SELECT pc.LastName, pc.FirstName, pc.EmailAddress FROM Sales.SalesOrderHeader AS ssoh INNER JOIN Sales.SalesOrderDetail AS ssod ON ssoh.SalesOrderID = ssod.SalesOrderID INNER JOIN Sales.Customer AS sc ON ssoh.CustomerID = sc.CustomerID INNER JOIN Sales.Individual AS si ON sc.CustomerID = si.CustomerID INNER JOIN Person.Contact AS pc ON si.ContactID = pc.ContactID WHERE ssoh.OrderDate BETWEEN @StartDate AND @EndDate GROUP BY pc.LastName, pc.FirstName, pc.EmailAddress; GO
Next we’ll want to set up some roles:
-- this is our internal sales personnel CREATE ROLE internal_sales_team; GO -- these are sales people in the field CREATE ROLE field_sales_team; GO
Now we’ll create users for those roles:
CREATE USER Iris WITHOUT LOGIN; GO CREATE USER Frank WITHOUT LOGIN; GO EXEC sp_addrolemember N'internal_sales_team', N'Iris'; EXEC sp_addrolemember N'field_sales_team', N'Frank'; GO GRANT EXECUTE ON SCHEMA :: Sales TO internal_sales_team; GO
Now let’s test this to see how it works:
EXECUTE AS USER = N'Iris'; GO EXEC Sales.GetCustomersWithOrdersBetweenDates '20040101', '20040601'; GO REVERT GO EXECUTE AS USER = N'Frank'; GO EXEC Sales.GetCustomersWithOrdersBetweenDates '20040101', '20040601'; GO REVERT GO
Iris can successfully execute the stored procedure, despite her user not having access to the Sales schema because she is a member of the internal_sales_team role which does have execute permissions on the Sales schema. However, Frank cannot execute the stored procedure since the field_sales_team role does not have access to the Sales schema. If you try to run the SQL from this stored procedure as either Frank or Iris the SQL will fail since neither user has select permissions on the Sales or Person schemas.
As I mentioned earlier, through a careful combination of users, logins, roles, Windows users, and Windows groups you can assemble a very secure, robust security infrastructure in SQL Server that can handle a variety of tasks while make your administrative life a lot easier.
This is Jeremiah
I live in Portland, OR. I have two dogs.
I recently received a Master's of Science in Computer Science from Portland State University.
I'm was Microsoft MVP from 2009 - 2018 with a pile of certifications. Somewhere along the way, I wrote a database client for Riak and then handed it off to the community.