ユーザー権限を Graph Database で管理する

##はじめに

この投稿は英語ブログの記事 [Case Study: Manage User Privileges Using a Graph](https://medium.com/oracledevs/case-study-manage-user-privileges-using-a-graph-672d0d9dfe5c) の日本語版です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706384/26cb3fff-3952-f4a3-1c9e-2ea17f4db5aa.png)

柔軟なアクセス・コントロールとセキュリティを両⽴する必要性から、[グラフ・データベース](https://www.oracle.com/big-data/what-is-graph-database/)の活⽤の可能性が注⽬されています。ユーザーの属する権限グループを多段階に構成した場合でも、グラフを使うことで、あるユーザーの上の階層の権限グループを全て辿って確認するといったことが簡単にできます。

⼀例として、AMENIDY の [Cybele Safe Mail](https://cybele.cloud/) というサービスは、⾼度な暗号技術とデータの受け渡し情報を管理するデータベースを⽤いて、メールの添付ファイルを⾃動的に暗号化するソリューションです。このシステムでは、グラフに対する宣⾔的クエリといったグラフ・データベース機能、数⼗億エッジのグラフに対する安定したスケーラビリティ、加えてデータベースの確⽴されたセキュリティ機能が必要であることから、Oracle Database の [Property Graph 機能](https://www.oracle.com/database/graph/) が活⽤されています。

この記事では、メールの送受信という関係性をグラフ・データベースを⽤いて管理する際の、基本的なアイディアを説明します。ユーザーへのインタビューを元に書き起こしたものであるため、**実際のシステムの動作や、データモデル、クエリの内容はここに⽰されているものとは異なります**ので、ご了承ください。実際のシステムではより多くの関係性や条件を考慮する必要があるため、グラフを使った直感的なモデルやクエリが更に活躍すると推測されます。

##グラフの構成
ここで仮定するグラフでは、ユーザーとそのユーザーが属するグループ、ユーザーが送ったメールとそこに添付されたファイルがそれぞれノードとして扱われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706384/acfe0cca-dc66-6af1-94ef-dd8c19c9790b.png)
さらに、メールからはひとりの送信者と複数の受信者（ユーザーの場合もあればグループの場合もあります）にエッジが張られることで、そのメールの送受信の情報が格納されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706384/86a80f35-7e1d-515c-f64d-2fe236715262.png)

 
##グラフにノードやエッジを追加する
まず、メールが送信されたときの動作を考えてみます。あるユーザーが3つの添付ファイルを持つ1通のメールを送ったときには、以下の2つの PGQL クエリが発⾏されると想定できます。PGQL クエリの記法については https://pgql-lang.org/ を参照してください。
はじめの PGQL クエリは、ユーザー `user_01` に1つのメール・ノードを追加しています。このノードはラベルを `mail` とすることで、それがメール・ノードであることを⽰しており、さらに名前が `mail_01` であるというプロパティを加えています。エッジのラベルは `sent_by` となっています。

```sql
INSERT VERTEX v LABELS (mail) PROPERTIES (v.name = 'mail_01'), 
       EDGE e BETWEEN v AND u LABELS (sent_by) 
FROM MATCH (u:user) WHERE u.email = 'user_01@example.com'
```
次の PGQL クエリは、先ほど追加されたメール・ノードに添付ファイル・ノードを3つ追加しています。

```sql
INSERT VERTEX v1 LABELS (attachment) PROPERTIES (v1.name = 'attachment_01'), 
       VERTEX v2 LABELS (attachment) PROPERTIES (v2.name = 'attachment_02'), 
       VERTEX v3 LABELS (attachment) PROPERTIES (v3.name = 'attachment_03'), 
       EDGE e1 BETWEEN v1 AND m LABELS (attached_on), 
       EDGE e2 BETWEEN v2 AND m LABELS (attached_on),        
       EDGE e3 BETWEEN v3 AND m LABELS (attached_on) 
FROM MATCH (m:mail) 
WHERE m.name = 'mail_01'; 
```
この時点で作成されたグラフは次のように可視化することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706384/83d7dbe5-0ec1-0e9a-d141-a8cdcdf08aae.png)

さらに、このメールがどのユーザーに対して送信されたかを格納します。送信先ユーザーがこのデータベースに含まれていない場合には先に追加しておく必要があります。次の2つの PGQL クエリでは、ある送信先のユーザー・ノードがあるグループに追加され、そのユーザーとメールにエッジを追加しています。送信先のユーザー・ノードを作成し、グループ・ノードに `belongs_to` エッジで接続します。

```sql
INSERT VERTEX v LABELS (user) PROPERTIES (v.name = 'user_02@example.com'), 
       EDGE e BETWEEN v AND g LABELS (belongs_to) 
FROM MATCH (g:group) 
WHERE g.email = 'group_01@example.com'
```
メールノードから、新たに追加されたユーザーノードに `sent_to` エッジを追加しています。

```sql
INSERT EDGE e BETWEEN m AND u LABELS (sent_to) 
FROM MATCH (m:mail), MATCH (u:user) 
WHERE m.name = 'mail_01' AND u.email = 'user_02@example.com' 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706384/a58654d4-e908-2f06-d9d2-48f707bd60c3.png)

##グラフ上のつながりを確認する
メールの受信者は添付ファイルの開封時にサーバーから鍵を取得します。そうすることで、ユーザーは暗号化されたデータとその鍵を別々の経路を使って安全に⼊⼿することができます。
このときサーバー側では、次のようなクエリを⽤いてこのユーザーにメールが送信されたかどうかを確認します。

```sql
SELECT m.name FROM MATCH (a)-[:attached_on]->(m)-[:sent_to]->(u) 
WHERE a.name = 'attachment_01' 
  AND u.email = 'user_02@example.com'"
```
JShell 上で出⼒される結果：

```
+---------+ 
| NAME    | 
+---------+ 
| mail_01 | 
+---------+ 
```
レコードが返されればパスが存在していることを意味します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706384/b90e4cb6-e4a0-3be7-c6f3-bb4f53abf43f.png)

もしもメールがユーザではなく所属グループに送信されたものであれば、グループ・ノードを辿ることでそれが確認できるはずです。このメールの送信先を変更してみましょう。
まずは、`sent_to` エッジを削除します。

```sql
DELETE e 
FROM MATCH (m:mail)-[e:sent_to]->(u:user) 
WHERE m.name = 'mail_01' AND u.email = 'user_02@example.com' 
```
グループ・ノードに向けて、再度新たな `sent_to` エッジを作成します。

```sql
INSERT EDGE e BETWEEN m AND g LABELS (sent_to) 
FROM MATCH (m:mail), MATCH (g:group) 
WHERE m.name = 'mail_01' AND g.email = 'group_02@example.com' 
```

`MATCH` 句のパターンにグループ・ノードを含めて検索すると正しく結果が返されます。

```sql
SELECT m.name 
FROM MATCH (a)-[:attached_on]->(m)-[:sent_to]->(g)<-[:belongs_to]-(u) 
WHERE a.name = 'attachment_01' 
  AND u.email = 'user_02@example.com' 
```
JShell 上で出⼒される結果：

```
+---------+ 
| NAME    | 
+---------+ 
| mail_01 | 
+---------+ 
```

レコードが返されたのでパスが存在しています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706384/d8f00a58-096b-8a82-925a-40e0d41bb716.png)
グループノードが不特定数の階層を持っていた場合でも到達可能性を確認できます。例えば、次のクエリでは5階層までを遡り、これらのグループノードがメールノードにつながっているかどうかも確認することができます。

```sql
SELECT m.name 
FROM MATCH (a)-[:attached_on]->(m)-[:sent_to]->(g)<-/:belongs_to{1,5}/-(u) 
WHERE a.name = 'attachment_01' 
  AND u.email = 'user_02@example.com' 

```

##まとめ
この記事では、メールの送受信というユーザー・アクティビティを Oracle Database の Property Graph 機能を使って管理する例を紹介しました。グラフにより多くの関係性や条件を持たせることによって、メール以外のユーザー間のアクティビティを格納することもできますし、添付ファイルの開封期限をノードやエッジのプロパティに持たせて制御する、といった拡張も容易です。様々なサービスにおいて、柔軟なアクセス・コントロールがより迅速に導⼊できるようになることが期待されます。

##リンク
+ AMENIDY Cybele Safe Mail: https://cybele.cloud/ （AMENIDY はセキュリティ・ソリューションを提供している日本の企業で、Oracle Cloud のユーザーです）
+ Oracle Database Property Graph Feature: https://www.oracle.com/database/graph/
+ 17 Use Cases for Graph Databases and Graph Analytics:
https://www.oracle.com/cloud/solutions/use-cases-for-graph-databases-and-graph-analytics-ebook/