0

私はmssql2008 r2 SQLサーバーを持っています

問題: ユーザーはテーブルに対していくつかの列権限を持っています。彼は、テーブルの列の一部 (すべてではない) を更新できました。権限を侵害しないように UPDATE ステートメントを作成する必要があります。できれば動的クエリなしで。

MSSQL サーバーにこの機能はありますか?

4

1 に答える 1

2

動的 SQL (またはアプリまたは API レイヤーでの動的クエリの構築) なしですか? とても綺麗にはならないと思います。UPDATE コマンドには、影響を受ける列に対してユーザーがどのような権限を持っているかについての固有の知識はありません。クエリをエンジンに送信し、最善を尽くします。ユーザーがすべての列に対する権限を持っていない場合、意図したステートメントを変更してエラーを回避しようとせずに、エラーを返します。意図したすべての列が更新されていないにもかかわらず、更新を続けるのは実際には非常に悪いことだと思います。

とはいえ、次のようなことができると思いますが、まったくきれいにはなりません。実際、データベースプリンシパルに依存していなければ、はるかに簡単になります。

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...
;

これはまさにあなたが求めているものではありません。技術的には、列を更新しますが、それ自体に設定します (したがって、たとえば、トリガーで更新された列として示されます) が、入力がテーブルに適用されないようにします。また、明示的な GRANT UPDATE または DENY UPDATE 以外の方法で指定されたユーザーまたはロールに付与された権限についてもチェックしませんでした。もちろん、チェックする列が複数ある場合、これを管理するのはまったく楽しいことではありません。

WHEN 句に他の条件を追加することもできます。たとえば、チェックを明示的にバイパスしたい dbo ( who ) またはユーザーのチェックを回避するには、次のようにします。

CASE 
  WHEN DATABASE_PRINCIPAL_ID() = 1 THEN @bar
  WHEN SUSER_SNAME = 'some_user' THEN @bar
  WHEN (...stuff from above...)
  ELSE bar
END
-- WHERE...
;
于 2012-01-26T05:01:12.280 に答える