|
| 1 | +-- Demonstrate Row Level Security |
| 2 | + |
| 3 | + |
| 4 | +USE master; |
| 5 | +GO |
| 6 | + |
| 7 | +IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'GreatLakesUser') |
| 8 | +BEGIN |
| 9 | + CREATE LOGIN GreatLakesUser |
| 10 | + WITH PASSWORD = N'SQLRocks!00', |
| 11 | + CHECK_POLICY = OFF, |
| 12 | + CHECK_EXPIRATION = OFF, |
| 13 | + DEFAULT_DATABASE = WideWorldImporters; |
| 14 | +END; |
| 15 | +GO |
| 16 | + |
| 17 | +IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'Website') |
| 18 | +BEGIN |
| 19 | + CREATE LOGIN Website |
| 20 | + WITH PASSWORD = N'SQLRocks!00', |
| 21 | + CHECK_POLICY = OFF, |
| 22 | + CHECK_EXPIRATION = OFF, |
| 23 | + DEFAULT_DATABASE = WideWorldImporters; |
| 24 | +END; |
| 25 | +GO |
| 26 | + |
| 27 | +USE WideWorldImporters; |
| 28 | +GO |
| 29 | + |
| 30 | +CREATE USER GreatLakesUser FOR LOGIN GreatLakesUser; |
| 31 | +GO |
| 32 | + |
| 33 | +CREATE USER Website FOR LOGIN Website; |
| 34 | +GO |
| 35 | + |
| 36 | +ALTER ROLE [Great Lakes Sales] ADD MEMBER GreatLakesUser; |
| 37 | +GO |
| 38 | + |
| 39 | +-- Ensure that the policy has been applied |
| 40 | +EXEC [Application].Configuration_ApplyRowLevelSecurity; |
| 41 | +GO |
| 42 | + |
| 43 | +-- The function that has been applied is as follows: |
| 44 | +-- |
| 45 | +-- CREATE FUNCTION [Application].DetermineCustomerAccess(@CityID int) |
| 46 | +-- RETURNS TABLE |
| 47 | +-- WITH SCHEMABINDING |
| 48 | +-- AS |
| 49 | +-- RETURN (SELECT 1 AS AccessResult |
| 50 | +-- WHERE IS_ROLEMEMBER(N'db_owner') <> 0 |
| 51 | +-- OR IS_ROLEMEMBER((SELECT sp.SalesTerritory |
| 52 | +-- FROM [Application].Cities AS c |
| 53 | +-- INNER JOIN [Application].StateProvinces AS sp |
| 54 | +-- ON c.StateProvinceID = sp.StateProvinceID |
| 55 | +-- WHERE c.CityID = @CityID) + N' Sales') <> 0 |
| 56 | +-- OR (ORIGINAL_LOGIN() = N'Website' |
| 57 | +-- AND EXISTS (SELECT 1 |
| 58 | +-- FROM [Application].Cities AS c |
| 59 | +-- INNER JOIN [Application].StateProvinces AS sp |
| 60 | +-- ON c.StateProvinceID = sp.StateProvinceID |
| 61 | +-- WHERE c.CityID = @CityID |
| 62 | +-- AND sp.SalesTerritory = SESSION_CONTEXT(N'SalesTerritory')))); |
| 63 | +-- GO |
| 64 | + |
| 65 | +-- The security policy that has been applied is as follows: |
| 66 | +-- |
| 67 | +-- CREATE SECURITY POLICY [Application].FilterCustomersBySalesTerritoryRole |
| 68 | +-- ADD FILTER PREDICATE [Application].DetermineCustomerAccess(DeliveryCityID) |
| 69 | +-- ON Sales.Customers, |
| 70 | +-- ADD BLOCK PREDICATE [Application].DetermineCustomerAccess(DeliveryCityID) |
| 71 | +-- ON Sales.Customers AFTER UPDATE; |
| 72 | +-- GO |
| 73 | + |
| 74 | +SELECT * FROM sys.database_principals; -- not the role for Great Lakes and the user for Great Lakes |
| 75 | +GO |
| 76 | + |
| 77 | +SELECT * FROM Sales.Customers; -- and note count |
| 78 | +GO |
| 79 | + |
| 80 | +GRANT SELECT, UPDATE ON Sales.Customers TO [Great Lakes Sales]; |
| 81 | +GRANT SELECT ON [Application].Cities TO [Great Lakes Sales]; |
| 82 | +GRANT SELECT ON [Application].Countries TO [Great Lakes Sales]; |
| 83 | +GO |
| 84 | + |
| 85 | +EXECUTE AS USER = 'GreatLakesUser'; |
| 86 | +GO |
| 87 | + |
| 88 | +-- Now note the count and which rows are returned |
| 89 | +-- even though we have not changed the command |
| 90 | + |
| 91 | +SELECT * FROM Sales.Customers; |
| 92 | +GO |
| 93 | + |
| 94 | +-- where are those customers? |
| 95 | +-- note the spatial results tab |
| 96 | + |
| 97 | +SELECT c.Border |
| 98 | +FROM [Application].Countries AS c |
| 99 | +WHERE c.CountryName = N'United States' |
| 100 | +UNION ALL |
| 101 | +SELECT c.DeliveryLocation |
| 102 | +FROM Sales.Customers AS c; |
| 103 | +GO |
| 104 | + |
| 105 | +-- updating rows that are accessible to a non-accessible row is blocked |
| 106 | + |
| 107 | +UPDATE Sales.Customers -- Attempt to update |
| 108 | +SET DeliveryCityID = 3 -- to a city that is not in the Great Lakes Sales Territory |
| 109 | +WHERE DeliveryCityID = 32887; -- for a customer that is in the Great Lakes Sales Territory |
| 110 | + |
| 111 | +REVERT; |
| 112 | +GO |
| 113 | + |
| 114 | +-- Remove the user from the role |
| 115 | +ALTER ROLE [Great Lakes Sales] DROP MEMBER GreatLakesUser; |
| 116 | +GO |
| 117 | + |
| 118 | +-- Instead of permission for a role, let's give permissions to the website user |
| 119 | +GRANT SELECT, UPDATE ON Sales.Customers TO [Website]; |
| 120 | +GRANT SELECT ON [Application].Cities TO [Website]; |
| 121 | +GRANT SELECT ON [Application].Countries TO [Website]; |
| 122 | +GO |
| 123 | + |
| 124 | +-- Open the second RLS demo window and follow the instructions there |
| 125 | + |
| 126 | +-- Finally, tidy up (optional) |
| 127 | + |
| 128 | +REVOKE SELECT, UPDATE ON Sales.Customers FROM [Great Lakes Sales]; |
| 129 | +REVOKE SELECT ON [Application].Cities FROM [Great Lakes Sales]; |
| 130 | +REVOKE SELECT ON [Application].Countries FROM [Great Lakes Sales]; |
| 131 | +REVOKE SELECT, UPDATE ON Sales.Customers FROM [Website]; |
| 132 | +REVOKE SELECT ON [Application].Cities FROM [Website]; |
| 133 | +REVOKE SELECT ON [Application].Countries FROM [Website]; |
| 134 | +GO |
| 135 | + |
| 136 | +EXEC [Application].Configuration_RemoveRowLevelSecurity; |
| 137 | +GO |
| 138 | + |
| 139 | +DROP USER GreatLakesUser; |
| 140 | +GO |
| 141 | + |
| 142 | +DROP USER Website; |
| 143 | +GO |
| 144 | + |
| 145 | +USE master; |
| 146 | +GO |
| 147 | + |
| 148 | +DROP LOGIN GreatLakesUser; |
| 149 | +GO |
| 150 | + |
| 151 | +DROP LOGIN Website; |
| 152 | +GO |
| 153 | + |
| 154 | +USE tempdb; |
| 155 | +GO |
| 156 | + |
0 commit comments