0

I have mssql2008 r2 sql server

The problem: User has some column permissions on the table. He could update some of the columns of the table (not all). We need to create UPDATE statement so that it will not violate permissions. Preferably without dynamic query.

Is there this ability in MSSQL server?

Yuriy Vikulov
  • 2,469
  • 5
  • 25
  • 32

1 Answers1

2

Without dynamic SQL (or dynamic query construction in the app or API layer)? I don't think it will be very pretty. The UPDATE command doesn't have any inherent knowledge of what permissions the user might have on the affected column(s). It is going to submit the query to the engine and hope for the best. If the user doesn't have permissions on all the columns, it's going to return an error, not try to circumvent that by altering the intended statement. I think this would actually be a very bad thing to continue with the update even though not all intended columns have been updated.

That all said, I suppose you could do something like this, but it is not going to be pretty at all - in fact it will be a lot easier if you are not relying on database principals:

DECLARE 
    @dpid INT = DATABASE_PRINCIPAL_ID(),
    @obj  INT = OBJECT_ID('dbo.foo'),
    @col  SYSNAME = N'bar';

UPDATE dbo.foo SET bar = CASE 
  WHEN EXISTS -- check they've been granted UPDATE at column or table level:
  (
    SELECT 1 
      FROM sys.database_permissions AS dp
      INNER JOIN sys.objects AS o 
        ON dp.major_id = o.[object_id]
      LEFT OUTER JOIN  sys.columns AS c
        ON dp.minor_id = COALESCE(c.column_id, 0)
      WHERE dp.grantee_principal_id = @dpid
      AND o.[object_id] = @obj
      AND (c.name = @col OR c.column_id IS NULL)
      AND dp.[permission_name] = 'UPDATE'
      AND dp.[state] = 'G' -- GRANT
  ) 
  AND NOT EXISTS -- since DENY trumps GRANT, make sure that doesn't also exist:
  (
    SELECT 1
      FROM sys.database_permissions AS dp
      INNER JOIN sys.objects AS o
        ON dp.major_id = o.[object_id]
      LEFT OUTER JOIN  sys.columns AS c
        ON dp.minor_id = COALESCE(c.column_id, 0)
      WHERE dp.grantee_principal_id = @dpid
      AND o.[object_id] = @obj
      AND (c.name = @col OR c.column_id IS NULL)
      AND dp.[permission_name] = 'UPDATE'
      AND dp.[state] = 'D' -- DENY
)
THEN @bar ELSE bar END
-- WHERE...
;

This isn't exactly what you're asking for; technically it updates the column but sets it to itself (so it will still be indicated as an updated column in a trigger, for example) but it prevents the input from being applied to the table. I also did not check against permissions granted in ways other than an explicit GRANT UPDATE or DENY UPDATE to the specified user or role - for example GRANT ALL, or permissions inherited by AD group membership, can complicate this. Of course it is not going to be much fun at all to manage this if you have multiple columns to check.

You may want to add other conditionals to the WHEN clause, e.g. to avoid the check for dbo (who ) or users you want to explictly bypass the check, you could have:

CASE 
  WHEN DATABASE_PRINCIPAL_ID() = 1 THEN @bar
  WHEN SUSER_SNAME = 'some_user' THEN @bar
  WHEN (...stuff from above...)
  ELSE bar
END
-- WHERE...
;
Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
  • yes, sure, UPDATE does not know, but can I tell UPDATE not to change (touch) specific columns explicit? – Yuriy Vikulov Jan 26 '12 at 05:07
  • Yes, by not including them in the clause? You can't say `UPDATE foo SET bar = bar + 1 WITH BUT_DO_NOT_TOUCH_THIS`... – Aaron Bertrand Jan 26 '12 at 05:08
  • don't laugh at me :-) Ok, I see, i do understood your answer and comment. Maybe you can advice me what should I do in his situation (based on your point of view), can't you? – Yuriy Vikulov Jan 26 '12 at 05:16
  • Sure, check which columns the user does have access to, and build the query dynamically. You'll have to let them know that not all of their data was accepted. Or, present the user with only the ability to provide input for the columns he/she can change (e.g. by making the form fields they can't edit grayed out/read only). Then there are no surprises. – Aaron Bertrand Jan 26 '12 at 05:20