Question

We're using stored procedures to restrict the access of some of our database users. They need access to specific parts of the database (not just tables/views, but also specific rows), and the sproc should check if the user is allowed to see the table rows he's requesting.

To store the authorization rules, we're planning to use a table dbo.AuthRules like that:

|ID|UserId  |AccessFrom  |AccessTo    | ...
===============================================
| 1|       1| 01.01.2013 | 31.12.2013 | ...
| 2|       2| 31.05.2012 | 31.12.2015 | ...

The stored procedure would then query that table to check if the current user has access to the requested data. To make it clear: We cannot just use GRANT PERMISSION because we need fine-grained access rules down to the rows in the DB.

We're not sure about the UserId column. The best solution would be some kind of foreign key to the system view sys.database_principals, but there are no foreign keys to views.

  • Should we just store the principal_id column of sys.database_principals, without any constraint?
  • Would it be better to store the name column instead of principal_id?
  • Are there other options to store a reference to the DB user?
Était-ce utile?

La solution

Q: Should we just store the principal_id column of sys.database_principals, without any constraint?

A: If you are evaluating only a constraint-based solution, you don't really have any choice. However, if you are willing to write a trigger, that would be the way to at least 'virtually' create constraint. Otherwise you would have to reference system sysowners table and this isn't possible without some hacking.

Q: Would it be better to store the name column instead of principal_id?

A: To answer that you have to ask the what-if question. What if principal_id changes? What if name changes? What about updates/deletes? I would always rather have an id-based solution.

Q: Are there other options to store a reference to the DB user?

A: As stated in answer#1, I don't think there are any possibilities except creating a trigger...

Autres conseils

Consider this design method.

USE "master";

-- Create our test logins (don't forget to tidy these up later!)
CREATE LOGIN "batman"
  WITH PASSWORD = N''
     , CHECK_EXPIRATION = OFF
     , CHECK_POLICY = OFF;

CREATE LOGIN "robin"
  WITH PASSWORD = N''
     , CHECK_EXPIRATION = OFF
     , CHECK_POLICY = OFF;

USE "playdb";

-- Create the corresponding users
CREATE USER "batman"
  FOR LOGIN "batman";

CREATE USER "robin"
  FOR LOGIN "robin";

-- Grant them some select permissions
EXEC sp_addrolemember N'db_datareader', N'batman';
EXEC sp_addrolemember N'db_datareader', N'robin';

-- Create our "user access" table
DECLARE @users table (
   username    sysname NOT NULL
 , access_from date    NOT NULL DEFAULT '1900-01-01' -- Deliberately not allowing nulls and setting the widest possible value range
 , access_to   date    NOT NULL DEFAULT '9999-12-31' --   this will make our queries so much easier!
);

-- Give one user full permissions
INSERT INTO @users (username)
  VALUES ('batman');

-- Limit what this chappy can see
INSERT INTO @users (username, access_from, access_to)
  VALUES ('robin', '2011-01-01', '2012-01-01');

-- Example data table
DECLARE @data table (
  date_field date NOT NULL
);
-- ...with example data
INSERT INTO @data (date_field)
  VALUES ('2010-01-01')
       , ('2011-01-01')
       , ('2012-01-01')
       , ('2012-01-01')
       , ('2013-01-01')
       , ('2014-01-01');

-- Let's mimic our full access user
SETUSER 'batman';

SELECT System_User As current_username
     , *
FROM   @data As data
WHERE  EXISTS ( -- Limit the records to only those this user is allowed to see
         SELECT *
         FROM   @users
         WHERE  username = System_User
         AND    data.date_field >= access_from
         AND    data.date_field <  access_to
       );

SETUSER; -- Reset user

-- Imitate the chap with restricted access
SETUSER 'robin';

-- Exacty same query as before
SELECT System_User As current_username
     , *
FROM   @data As data
WHERE  EXISTS (
         SELECT *
         FROM   @users
         WHERE  username = System_User
         AND    data.date_field >= access_from
         AND    data.date_field <  access_to
       );
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top