はじめに
OAC(Oracle Analytics Cloud)のセマンティック・モデラーを使用して行レベルセキュリティを実現する方法を紹介します。
セマンティック・モデラーを使用するので、OAC Enterpriseでのみ有効な方法です。
検索対象のデータとは別にセキュリティ用のテーブルを作成し、ユーザーとユーザーの参照範囲を管理します。その情報をセッション変数に格納して、ユーザーの所属するアプリケーション・ロールに対するフィルターの中で使用する方法を紹介します。
他にも、データベース側で行レベルセキュリティを実装する方法もありますが、この記事では対象外です。
実現のイメージは、こんな感じです。
OAC 2025 May Updateを使用しました。
1. 行レベルセキュリティの基本
1.1 行レベルセキュリティとは
データへのアクセスを個々のユーザーやアプリケーションロールに対して、行レベルで制限するセキュリティです。
データレベル・セキュリティということもあります。
ユーザーが見るべきデータのみを表示し、機密情報を保護するために使用されます。
1.2 OACでの行レベルセキュリティの必要性
ダッシュボードやレポートを多数のユーザーが共有しつつ、それぞれ異なるデータ範囲を見せたいとます。
例えば、営業担当者には自分の担当地域のみ、マネージャには部門全体のデータを表示させたいといったケースです。
行レベルセキュリティを設定すれば、ログインしたユーザーの属性に応じて、自動的にデータフィルターが適用されます。
共通の分析コンテンツを提供しながら、ユーザーごとにパーソナライズされたデータビューを実現します。
これにより、データやコンテンツの複製を避け、一貫した分析環境を提供できます。
それぞれの権限単位でダッシュボードやレポートを作成しなくてもいいので、生産性が高くなります。
2. セマンティック・モデラーでの行レベルセキュリティの実装
セマンティック・モデラーを使用した開発について、詳しく知りたい方はこちらの記事を参考にしてください。
- Oracle Analytics Cloud:ブラウザでリポジトリ作成
- セマンティックモデラー(Semantic Modeler)を試す。
- OAC:セマンティックモデラーによるセマンティックモデルのつくりはじめ
- 公式ドキュメント
2.1 セキュリティ用テーブルの作成
検索用データとしてADW(Autonomouse Data Warehouse)に付属するSHスキーマの一部を使用しますが、それとは別にユーザーの権限を判定するためのセキュリティ用テーブルを準備します。
SHスキーマとは別のスキーマで、次のSQLを実行します。
create table row_level_sec (
username varchar2(40),
channel_id number);
insert into row_level_sec values('OACUser1', 2);
insert into row_level_sec values('OACUser1', 3);
insert into row_level_sec values('OACUser1', 9);
insert into row_level_sec values('OACUser2', 4);
insert into row_level_sec values('OACUser2', 5);
commit;
こんな感じの表ができます。
USERNAME | CHANNEL_ID |
---|---|
OACUser1 | 2 |
OACUser1 | 3 |
OACUser1 | 9 |
OACUser2 | 4 |
OACUser2 | 5 |
USERNAMEには、OACにログインするユーザー名を、CHANNEL_IDにはそのユーザーに検索を許可するチャネルIDを指定します。
CHANNEL_IDが2,3,9のデータだけを検索できるユーザー(OACUser1)と、CHANNEL_IDが4,5のデータだけを検索できるユーザー(OACUser2)を登録しました。
2.2 セマンティック・モデルの編集
今回は、SHスキーマに含まれる表を使用した、簡単なセマンティックモデルを用意しました。
2.2.1 初期化ブロックを作成
初期化ブロックを作成します。
初期化ブロックとは、OACへのログイン時に実行される一連のSQLを定義したものです。
SQLによる実行結果をセッション変数に保持することができます。
名前の通り、セッションの間は変数の内容が保持されます。
「変数」タブに移動します。
「+」をクリックして、「初期化ブロックの作成」をクリックします。
任意の名前を入力し、タイプを「セッション」にして「OK」をクリックします。
「選択」をクリックして接続プールを指定します。
通常の検索に使用するのとは別に、初期化ブロック専用の接続プールを作成しておくことをおすすめします。
「+」をクリックして、変数を追加します。
「ROW_LEVEL_SEC」という名前に変更します。
画面右上の「保存」をクリックします。
同じ要領で、もう一つ初期化ブロックを作成します。
先ほどとは別の名前を入力し、タイプを「セッション」にして「OK」をクリックします。
「問い合わせの返し」を「変数名と値」にして、「Select文」にSQLを入力します。
次のSQLを参考にしてください(スキーマ名、表や列名は適宜変更してください)。
select 'ROW_LEVEL_SEC', channel_id from oac_sec.row_level_sec where upper(username)=upper('VALUEOF(NQ_SESSION.USER)')
変数「ROW_LEVEL_SEC」に検索した一つ以上の値が格納されます。
次に、このSQLを実行する際に使用する接続プールを指定するために「選択」をクリックします。
接続プールを指定します。
「問合せのテスト」をクリックします。
現在、ログイン中のユーザーが「OACUser1」でも「OACUser2」でもない場合は、このような結果になるはずです。
セキュリティテーブルに存在するユーザーの場合は、こうなります。
「依存性」タブに移動し、最初に作成した初期化ブロックを選択します。
ここまでの設定内容を保存します。
2.2.2 フィルターを設定
フィルターは、プレゼンテーション層または論理層に設定できます。
今回は、論理層に設定します。
論理表「販売実績」を含むSQLが実行される際に、フィルターが適用されるようにします。
- 論理レイヤーに移動します
- 論理ファクト表「販売実績」をダブルクリックします
- 「データ・フィルタ」タブに移動します
「追加」テキストボックスに「DV Consumer」と入力します。
途中まで注力すると、候補としてリストされるので、クリックします。
「DV Consumer」ロールに所属するユーザーが使用する際にフィルターを適用する準備ができました。
具体的なフィルター内容を設定するために「式エディタ」を開きます。
論理ファクト表「販売実績」は、SHスキーマの「SALES」表であり、これはファクト表です。
ファクト表の列に直接フィルターを設定することもできますが、ここでは「SALES」表のディメンション表である「CHANNELS」表にフィルターを設定します。
「CHANNELS」表は、論理ディメンション表「チャネル」として定義されています。
「変数」タブで、定義済みのセッション変数「ROW_LEVEL_SEC」をダブルクリックします。
「検証」して、「保存」をクリックし式エディタを閉じます。
これで、論理表「販売実績」が使用される際には、必ず論理表「チャネル」の論理列「チャネルID」にフィルターが設定されます。
「チャネル」と「販売実績」は結合されているので、結果的に「販売実績」の必要なデータのみがフィルターされます。
論理ディメンション表「チャネル」を単独で使用するクエリーには、このフィルターは適用されません。
そういったケースでもフィルターを適用したい場合は、論理ディメンション表「チャネル」に対しても同様のフィルターを設定します。
保存します。
セマンティック・モデルをデプロイします。
BI Service Administratorロールに対しては、フィルタが適用されません。
行レベルセキュリティを適用したいユーザーに、このロールが割り当てられていないことを確認してください。
3. 動作確認
フィルターが自動的に適用されるユーザー(OACUser1)でログインします。
チャネル別の販売金額で、ビジュアライゼーションを作成してみました。
発行されたSQLです。
WITH
SAWITH0 AS (select sum(T12.AMOUNT_SOLD) as c1,
sum(T12.QUANTITY_SOLD) as c2,
T5.CHANNEL_CLASS as c3,
T5.CHANNEL_DESC as c4,
T5.CHANNEL_ID as c5,
T5.CHANNEL_CLASS_ID as c6
from
SH.CHANNELS T5,
SH.SALES T12
where ( T5.CHANNEL_ID = T12.CHANNEL_ID and (T5.CHANNEL_ID in (2.0, 3.0, 9.0)) and (T12.CHANNEL_ID in (2.0, 3.0, 9.0)) )
group by T5.CHANNEL_CLASS, T5.CHANNEL_CLASS_ID, T5.CHANNEL_DESC, T5.CHANNEL_ID)
select D1.c1 as c1, D1.c2 as c2, D1.c3 as c3, D1.c4 as c4, D1.c5 as c5, D1.c6 as c6, D1.c7 as c7 from ( select 0 as c1,
D1.c3 as c2,
D1.c4 as c3,
D1.c2 as c4,
D1.c1 as c5,
D1.c5 as c6,
D1.c6 as c7
from
SAWITH0 D1
order by c2, c3 ) D1 where rownum <= 125001
T5.CHANNEL_ID in (2.0, 3.0, 9.0)
という条件が自動的に付与されているのがわかります。
セマンティック・モデラーでの設定では、「=」を使用しましたが、自動的に「in」に置き換わっています。
このワークブックを共有フォルダに保存します。
別のユーザー(OACUser2)でログインし、保存したワークブックを開きます。
さっきのユーザーとは別の行が検索されたことがわかります。
(検索条件がさっきと異なっています)
WITH
SAWITH0 AS (select sum(T12.AMOUNT_SOLD) as c1,
sum(T12.QUANTITY_SOLD) as c2,
T5.CHANNEL_CLASS as c3,
T5.CHANNEL_DESC as c4,
T5.CHANNEL_ID as c5,
T5.CHANNEL_CLASS_ID as c6
from
SH.CHANNELS T5,
SH.SALES T12
where ( T5.CHANNEL_ID = T12.CHANNEL_ID and (T5.CHANNEL_ID in (4.0, 5.0)) and (T12.CHANNEL_ID in (4.0, 5.0)) )
group by T5.CHANNEL_CLASS, T5.CHANNEL_CLASS_ID, T5.CHANNEL_DESC, T5.CHANNEL_ID)
select D1.c1 as c1, D1.c2 as c2, D1.c3 as c3, D1.c4 as c4, D1.c5 as c5, D1.c6 as c6, D1.c7 as c7 from ( select 0 as c1,
D1.c3 as c2,
D1.c4 as c3,
D1.c2 as c4,
D1.c1 as c5,
D1.c5 as c6,
D1.c6 as c7
from
SAWITH0 D1
order by c3, c2 ) D1 where rownum <= 125001
追加で、こういうビジュアライゼーションを作成してみました。
プレゼンテーションフォルダ「チャネル」の中にある列だけを使用しています。
これは、論理表「チャネル」と対応しており、論理表「チャネル」に対してはフィルタを設定していません。
なので、どのユーザーで実行してもフィルタなしでSQLが実行されます。
WITH
SAWITH0 AS (select distinct T5.CHANNEL_CLASS as c1,
T5.CHANNEL_DESC as c2
from
SH.CHANNELS T5)
select D1.c1 as c1, D1.c2 as c2, D1.c3 as c3 from ( select 0 as c1,
D1.c1 as c2,
D1.c2 as c3
from
SAWITH0 D1
order by c2, c3 ) D1 where rownum <= 125001
4. 考慮事項
4.1 パフォーマンスへの影響
行レベルセキュリティの適用は、パフォーマンスに影響する可能性があります。
データソース側の適切なインデックス作成が、パフォーマンス最適化の鍵になることもあります。
十分にテストをしてください。
4.2 キャッシュの挙動と注意点
行レベルセキュリティが適用されると、ユーザーごとに異なるSQLが発行されるため、クエリー結果のキャッシュを共有することが実質的に不可能になります。
これにより、システム全体のパフォーマンスがどのように変化するかを注意深く確認する必要があります。
4.3 セキュリティ設定のテスト
定義したセキュリティポリシーが意図した通りに適用されているかを確認するため、異なるテストユーザーを作成し、実行結果を確認します。
本番環境にデプロイする前に、網羅的な検証を通じて潜在的なセキュリティホールがないかどうか確認してください。
5. 関連情報