Databricks Unity Catalog で実現するアクセスコントロール

みなさん、こんにちは！
Unity Catalog では、スキーマやテーブル単位での通常の権限管理に加えて、行/列レベルでアクセスコントロールを行うことが可能です。

ユーザの権限に応じて特定の行を非表示にしたり、特定の列に対してマスキングをかけることができるため、専用のビューを作成したりする必要なく柔軟なアクセスコントロールを実現できます。

本記事では具体的な例とともに、Unity Catalog のアクセスコントロール機能についてご紹介します。

## 準備
まずは以下のSQLで`employee`テーブルを作成します。
```ruby:qiita.rb
-- Unity Catalog の main.default スキーマを使用
USE main.default;

-- employees テーブル作成
CREATE TABLE employees (
  id INT,
  name STRING,
  department STRING,
  country STRING,
  address STRING,
  email STRING,
  salary FLOAT
);

-- データ挿入
INSERT INTO employees (id, name, department, country, address, email, salary) VALUES
(1, 'John Doe', 'Engineering', 'USA', '123 Elm St', 'john.doe@example.com', 75000.00),
(2, 'Jane Smith', 'Marketing', 'Canada', '456 Maple Ave', 'jane.smith@example.com', 65000.00),
(3, 'Alice Jones', 'Sales', 'UK', '789 Oak Blvd', 'alice.jones@example.com', 70000.00),
(4, 'Bob Brown', 'HR', 'Australia', '101 Pine Rd', 'bob.brown@example.com', 60000.00),
(5, 'Carol White','Finance', 'Germany', '202 Birch Ln', 'carol.white@example.com', 80000.00);
```

上記を実行した結果の`employee`テーブルは以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/f63deb0e-1747-48d3-8ca1-d86668f7b486.png)

## 列レベルセキュリティ（Column-Level Security）
**列レベルセキュリティ**は列に対して適用されるアクセスコントロール機能です。

アクセスしようとするユーザやプリンシパルの権限に応じて、テーブルの特定の列に対し加工やマスキングを適用することが可能です。PIIなど機密性の高い情報を保護する目的で使用されます。

設定手順は以下の通りです。
### 1. 関数を作成
対象の列に対して適用する関数を作成します。

以下は人事部（HR）のメンバーのみが従業員の給料を見ることができ、それ以外は「**」でマスキングされるようにするための関数です。
```ruby:qiita.rb
-- 列マスク用の関数作成
CREATE OR REPLACE FUNCTION salary_mask(salary STRING)
RETURN IF(is_account_group_member('HR'), salary, "****");
```

### 2. 関数の適用
作成した関数をテーブルに適用します。
```ruby:qiita.rb
ALTER TABLE employees ALTER COLUMN salary SET MASK salary_mask;
```
上記の設定後にテーブルを確認すると、マスキングが適用されていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/e1b44bcf-2b49-4429-9c44-4c870b8b929c.png)

※たとえワークスペース管理者（admins）であっても、HRメンバーでなければ給料を見ることはできません。

## 行レベルセキュリティ（Row-Level Security）
**行レベルセキュリティ**は行に対して適用されるアクセスコントロール機能です。

列レベルセキュリティと同様に、行レベルでのアクセスコントロールを行うことができます。クエリ実行時にフィルターが適用され、アクセス権限に応じた結果を返します。

設定手順は以下の通りです。

### 0. 準備
応用的な行レベルセキュリティの実現にあたり、事前にマッピングテーブル`map_country_group`を作成しておきます。
```ruby:qiita.rb
-- 所属グループと国のマッピングテーブル作成
CREATE TABLE IF NOT EXISTS map_country_group (
  identity_group STRING,
  countries ARRAY
);

-- データ挿入
INSERT OVERWRITE map_country_group (identity_group, countries) VALUES
  ('EMPLOYEE_US', Array("USA","Canada")),
  ('EMPLOYEE_EU',  Array("UK","Germany")),
  ('EMPLOYEE_AU', Array("Australia"));
```

`map_country_group`は従業員をリージョンごとのグループにわけて対応させたものです。"USA"と"Canada"は`EMPLOYEE_US`、"UK"と"Germany"は`EMPLOYEE_EU`という風に対応させています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/68754f06-778c-4f52-8d26-034f93749057.png)

### 1. 関数の作成
対象の行に対して適用する関数を作成します。

以下は従業員が同一リージョン内の従業員の情報のみ参照でき、HRメンバーは全従業員の情報を見ることができるようにするための関数です。
```ruby:qiita.rb
-- 行フィルター用の関数作成
CREATE OR REPLACE FUNCTION country_filter(country STRING)
RETURN 
    is_account_group_member('HR') or
    EXISTS (
        SELECT 1
        FROM map_country_group
        WHERE is_account_group_member(identity_group) AND array_contains(countries, country)
    );
```

### 2. 関数の適用
作成した関数をテーブルに適用します。
```ruby:qiita.rb
ALTER TABLE employees SET ROW FILTER country_filter ON (country);
```
上記の設定後にテーブルを確認すると、フィルターが適用され何も表示されないことが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/0c8e5c0f-9a6e-45ed-97e0-80de26d70190.png)

`EMPLOYEE_US`グループを作成し、自分自身をユーザとして追加してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/c25507fa-aeb5-48b0-9098-b102ae8dcc17.png)

再度確認すると、USリージョンの従業員の結果のみが得られました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/9bf0eadd-3daf-40c1-9516-0eac43ab27f7.png)

同様にHRグループを作成し確認した結果は以下の通りとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/58c35a89-70d2-4081-8d3a-d036696ed4a5.png)
想定通り、全従業員のデータをそのままの形で確認できています。

## 設定確認
上記で設定した列マスクと行フィルターはカタログエクスプローラーからも確認可能です。
関数もテーブルと同じようにカタログ内に格納されており、定義など詳細を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/be385b78-53c2-47f2-bd2a-43a55305111d.png)
適用したフィルターはテーブル概要にも表示されており、どのカラムに適用したかなど視覚的にわかりやすいです。

## 列マスク、行フィルター削除
設定した列マスクと行フィルターは以下のコマンドで削除できます。

**列マスク削除**
```ruby:qiita.rb
ALTER TABLE employees ALTER COLUMN salary DROP MASK;
```

**行フィルター削除**
```ruby:qiita.rb
ALTER TABLE employees DROP ROW FILTER;
```

## 補足
ABAC（属性ベースアクセスコントロール） は機能の発表はされているものの現時点でリリースされておらず、プレビューでも利用できないようです。

ABAC により特定の属性を持つ全てのデータを対象にルールを作成し、一括でフィルターを適用できるようになるとのことなので、アクセスコントロールがさらに柔軟かつ強力なものになりそうです。今後の正式リリースが期待されます。

[Data + AI Summit 2024：Databricks Unity Catalogの最新情報](https://www.databricks.com/jp/blog/whats-new-databricks-unity-catalog-data-ai-summit-2024)

## 参考
* [Table ACL & Row and Column Level Security With Unity Catalog](https://www.databricks.com/resources/demos/tutorials/governance/table-acl-and-dynamic-views-with-uc)