Oracle Graph Server and Client の認証・認可機能が実装されたので試してみた

## 本記事の目的
 - Oracle Graph Server and Client 20.3.0.0.0 で新たに追加された認証・認可機能を試してみる
 - 本環境での認証プロバイダはOracle Databaseを使用することとする

Oracle Graph Server and Client 20.3 から、新たに認証・認可機能が追加されました。
https://blogs.oracle.com/oraclespatial/graph-server-and-client-203-is-now-available

今回は、認証・認可機能をどのように使用するのかを試してみます。

## 前提条件
 - 今回は、Oracle Databaseを認証プロバイダとして使用します

## Oracle Graph Server and Client の認証フロー

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/d4aab393-f6f3-7214-bf9b-143c23287e6b.png)

上の図は、Graph Server and Clientにおける認証フローです。（画像は[Document](https://docs.oracle.com/cd/E56133_01/latest/reference/security/authentication.html)より引用しました。）

1) ユーザは認証プロバイダ(本記事ではOracle Database)にログインします（本記事の手順4に該当します）
2) 認証プロバイダはユーザに認証トークンを発行します（本記事の手順4に該当します）
3) ユーザはセッションごとにGraph Server に認証トークンを送信します（本記事の手順5に該当します）
4) Graph Serverは認証トークンを照合し、ユーザ情報（権限）を取得します（本記事の手順5に該当します）

## シナリオ

本記事では、2人のユーザがそれぞれ下記に示す役割を担うことを想定します。

 - **graph_publisher**
 
グラフを作成し、Graph VizへPublishすることができる。
 
 - **graph_reader**

graph_publisherユーザがPublishしたグラフを読み取ることができる。

## 環境情報
```
$ cat /etc/oracle-release
Oracle Linux Server release 7.6
```

 - Oracle Graph Server and Client 20.3.0.0.0 をインストール済みであること
（インストール方法は[こちら](https://qiita.com/kk31108424/items/699eb86616c9cbd52f0a)を参考にしてください。)

## 手順

###1. 検証用ユーザ・ロール作成

```sql

/*データベースに接続*/
$ sqlplus /nolog
SQL> conn / as sysdba
Connected.

/*PDBを確認*/
SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 ORCL                           READ WRITE NO

SQL> alter session set container=orcl;

Session altered.

/*ユーザ作成*/
SQL> create user graph_publisher identified by graph_publisher
  2  default tablespace users
  3  temporary tablespace temp
  4  quota unlimited on users;

User created.

SQL> create user graph_reader identified by graph_reader
  2  default tablespace users
  3  temporary tablespace temp
  4  quota unlimited on users;

User created.


/*作成したユーザに権限付与*/
SQL> grant connect, resource to graph_publisher;

Grant succeeded.

SQL> grant connect, resource to graph_reader;

Grant succeeded.

/*Graph Server and Client で使用するロールを作成*/

SQL> CREATE ROLE rl_graph_publisher;

Role created.

SQL> CREATE ROLE rl_graph_reader;

Role created.

/*作成したユーザにロールを付与*/
SQL> grant rl_graph_publisher to graph_publisher;

Grant succeeded.

SQL> grant rl_graph_reader to graph_reader;

Grant succeeded.

SQL> exit

```
https://docs.oracle.com/en/database/oracle/oracle-database/20/spgdg/using-inmemory-analyst-oracle-database.html#GUID-FE3E0493-D720-4215-A28C-00F03F3BE5D6

### 2. ``/etc/oracle/graph/pgx.conf``を編集



```:/etc/oracle/graph/pgx.conf
$ cat /etc/oracle/graph/pgx.conf
{
  "allow_idle_timeout_overwrite": true,
  "allow_local_filesystem": true,
  "allow_task_timeout_overwrite": true,
 "authorization": [{
  "pgx_role": "GRAPH_PUBLISHER",
  "pgx_permissions": [{
    "grant": "PGX_SESSION_CREATE"  ★セッションを作成する権限
  },
   {
    "grant": "PGX_SESSION_NEW_GRAPH"  ★グラフを作成する権限
  },
   {
    "grant": "PGX_SESSION_ADD_PUBLISHED_GRAPH"  ★グラフをPublishする権限
  }]
}, {
  "pgx_role": "GRAPH_READER",
  "pgx_permissions": [{
    "grant": "PGX_SESSION_CREATE"  ★セッションを作成する権限
  },
     {
    "grant": "PGX_SESSION_GET_PUBLISHED_GRAPH"  ★Publishされたグラフを取得する権限
  }]
}],
  "datasource_dir_whitelist":[], 
  "enable_gm_compiler": true,
  "enterprise_scheduler_config": {
    "analysis_task_config": {
      "priority": "MEDIUM",
      "weight": "<no-of-CPUs>",
      "max_threads": "<no-of-CPUs>"
    },
    "fast_analysis_task_config": {
      "priority": "HIGH",
      "weight": 1,
      "max_threads": "<no-of-CPUs>"
    },
    "num_io_threads_per_task": "<no-of-CPUs>"
  },
  "graph_algorithm_language": "JAVA",
  "in_place_update_consistency_model": "ALLOW_INCONSISTENCIES",
  "java_home_dir": "<system-java-home-dir>",
  "max_active_sessions": 1024,
  "max_queue_size_per_session": -1,
  "max_snapshot_count": 0,
  "memory_cleanup_interval": 60,
  "path_to_gm_compiler": null,
  "preload_graphs": [],
  "pgx_realm": {
    "implementation": "oracle.pg.identity.DatabaseRealm",  ★認証プロバイダとしてOracle Databaseを指定
    "options": {
      "jdbc_url": "jdbc:oracle:thin:@localhost:1521/orcl",  ★PDBへのJDBC URLを指定
      "token_expiration_seconds": 14400,
      "connect_timeout_milliseconds": 10000,
      "max_pool_size": 64,
      "max_num_users": 512
    }
  },
  "release_memory_threshold": 0.25,
  "session_idle_timeout_secs": 14400,
  "session_task_timeout_secs": 0,
  "strict_mode": true,
  "tmp_dir": "/tmp"
}
```

https://docs.oracle.com/cd/E56133_01/latest/reference/config/pgx.html

### 3. Graph Server を起動

```
$ sudo systemctl start pgx
```
https://docs.oracle.com/en/database/oracle/oracle-database/20/spgdg/using-inmemory-analyst-oracle-database.html#GUID-03010F75-088C-4D6C-860A-D040597B2CF5

### 4. Token取得
Graph Server へのセッションを作成する際に必要となるTokenを取得します。

```
$ curl -X POST -H 'Content-Type: application/json' -d '{"username":"graph_publisher","password":"graph_publisher"}' http://localhost:7007/auth/token
```

以下のようなjsonが返ってきます(Tokenは[ドキュメント](https://docs.oracle.com/en/database/oracle/oracle-database/20/spgdg/using-inmemory-analyst-oracle-database.html#GUID-A08FC6AE-2807-4012-AD91-248062DA67C2)より引用)

```
{
  "token_type": "Bearer",
  "expires_in": 3600,
  "access_token": "eyJraWQiOiJEYXRhYmFzZVJlYWxtIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJwZ0FkbWluIiwicm9sZXMiOlsicmVzb3VyY2UiLCJjb25uZWN0IiwiZ3JhcGhfYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJvcmFjbGUucGcuaWRlbnRpdHkucmVzdC5BdXRoZW50aWNhdGlvblNlcnZpY2UiLCJleHAiOjE1OTAxMDU1MDV9.D14yGwvzW7zlyjxdiagknjB_wU3VSXnWKHFSYcLDkF2JclyMNE0MmtgJQ958BNFpvB-
ha0ODxn_H1mnIlk3Cq7aoLiXN9V2WoxpYPQSdTu1pU2cKo-NfKOJF_MaqnS-USw0XozovqtrEsnaWid8uF8vAS0WHt0Wm8nTtijoe99K__tvDgpYQH3cqicERPlBMRov9oOg-Rfuyg1o6CoGdgrNEMYG44RRJBXOBFCD15yJ2aUfMHU5fukAbh6aWmrkKbwwueUmgTdjhWlxooEwyF-C_LjksVPba2M5zRX-WOC6Zp8Lqxr6uqhxR1W4XpuQLLaD2Vw4OwpP7M5AldsS2MQ"
}

```

https://docs.oracle.com/en/database/oracle/oracle-database/20/spgdg/using-inmemory-analyst-oracle-database.html#GUID-A08FC6AE-2807-4012-AD91-248062DA67C2

### 5. Graph Server へ接続

```
$ /opt/oracle/graph/bin/opg-jshell -b http://localhost:7007

enter authentication token (press Enter for no token): <上記で取得したaccess_tokenをコピペ>
```
https://docs.oracle.com/en/database/oracle/oracle-database/20/spgdg/using-inmemory-analyst-oracle-database.html#GUID-A08FC6AE-2807-4012-AD91-248062DA67C2

### 6. グラフを作成しPublish

```java

opg-jshell> import oracle.pgx.common.auth.*;
opg-jshell> var g = session.<Integer>newGraphBuilder(IdType.INTEGER).addVertex(1).addVertex(2).addVertex(3).addEdge(1,1,2).addEdge(2,1,3).build();
g ==> PgxGraph[name=anonymous_graph_1,N=3,E=2,created=1598162420874]
opg-jshell> g.publish();
opg-jshell> g.grantPermission(new PgxUser("graph_reader"), PgxResourcePermission.READ); //publishしたグラフの読み取り権限をユーザ(graph_reader)に付与
```

### 7. Graph Vizから結果を確認

graph_readerユーザでGraph Vizに接続します。
**手順6において、graph_readerユーザに権限を付与しているので、graph_publisherユーザがPublishしたグラフを、graph_readerユーザも読み取ることができるようになっているはずです。**

まずはgraph_readerユーザでGraph Vizに接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/fdab9002-d683-f639-9327-6501bacf7dbb.png)

確認すると、下図のように、graph_publisherユーザでPublishしたグラフをgraph_readerユーザも読み取ることができています！
想定通りの動作が確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/6a45ac9a-cd1f-35b5-edac-1783183d1a74.png)

ちなみに、graph_publisherユーザに読み取り権限を付与しない状態でグラフを確認しようとすると、下図のようなエラーが出力されてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/9522b82d-1c16-45f8-d955-65ada5fd892a.png)


### Appendix. 実機確認時に遭遇したエラー集
#### ①java.lang.IllegalArgumentException: DatabaseRealm requires 'jdbc_url' to be configured in pgx.conf realm options

⇒認証プロバイダとしてOracle Databaseを指定した場合は、``/etc/oracle/graph/pgx.conf``にてjdbc_urlを指定する必要がある

```
（修正前）
  "pgx_realm": {
    "implementation": "oracle.pg.identity.DatabaseRealm",
    "options": {
      "jdbc_url": "<REPLACE-WITH-DATABASE-URL-TO-USE-FOR-AUTHENTICATION>",
      "token_expiration_seconds": 14400,
      "connect_timeout_milliseconds": 10000,
      "max_pool_size": 64,
      "max_num_users": 512
    }
  },
（修正後）
  "pgx_realm": {
    "implementation": "oracle.pg.identity.DatabaseRealm",
    "options": {
      "jdbc_url": "jdbc:oracle:thin:@localhost:1521/orcl",  ★
      "token_expiration_seconds": 14400,
      "connect_timeout_milliseconds": 10000,
      "max_pool_size": 64,
      "max_num_users": 512
    }
  },
```
https://docs.oracle.com/en/database/oracle/oracle-database/20/spgdg/using-inmemory-analyst-oracle-database.html#GUID-03010F75-088C-4D6C-860A-D040597B2CF5

#### ②java.lang.IllegalStateException: cannot connect to server; requested http://localhost:7007/version?extendedInfo=true and expected status 200, got 401 instead


⇒サーバに接続する際は、Databaseで作成したユーザにロールを付与し、Tokenを取得する必要がある
https://docs.oracle.com/en/database/oracle/oracle-database/20/spgdg/using-inmemory-analyst-oracle-database.html#GUID-A08FC6AE-2807-4012-AD91-248062DA67C2

```
java.util.concurrent.ExecutionException: java.lang.IllegalStateException: cannot connect to server; requested http://localhost:7007/version?extendedInfo=true and expected status 200, got 401 instead; response body = "{"type":"http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1","status":401,"title":"unauthorized","detail":"missing or invalid authentication token code: PGX-ERROR-1SL24XWK00U5H","o:errorCode":"PGX-ERROR-1SL24XWK00U5H"}"
```



#### ③oracle.pgx.common.auth.AuthorizationException

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/9522b82d-1c16-45f8-d955-65ada5fd892a.png)


⇒権限が不足しています。各操作に必要な権限は下記Documentを参照。
https://docs.oracle.com/cd/E56133_01/latest/reference/security/authorization.html

```
opg-jshell> g.publish();
|  Exception java.util.concurrent.CompletionException: oracle.pgx.common.auth.AuthorizationException: PgxUser(graph_publisher) does not have the SESSION_ADD_PUBLISHED_GRAPH permission required for this action code: PGX-ERROR-MXRML6YAEDJF
|        at CompletableFuture.reportJoin (CompletableFuture.java:412)
|        at CompletableFuture.join (CompletableFuture.java:2044)
|        at PgxFuture.join (PgxFuture.java:135)
|        at PgxGraph.publish (PgxGraph.java:4984)
|        at (#2:1)
|  Caused by: oracle.pgx.common.auth.AuthorizationException: PgxUser(graph_publisher) does not have the SESSION_ADD_PUBLISHED_GRAPH permission required for this action code: PGX-ERROR-MXRML6YAEDJF
|        at ExceptionMarshaler.toUnserializedException (ExceptionMarshaler.java:107)
|        at ExceptionMarshaler.unmarshal (ExceptionMarshaler.java:123)
|        at RemoteUtils.parseExceptionalResponse (RemoteUtils.java:126)
|        at HttpRequestExecutor.executeRequest (HttpRequestExecutor.java:198)
|        at HttpRequestExecutor.post (HttpRequestExecutor.java:62)
|        at RemoteCoreImpl$141.request (RemoteCoreImpl.java:3346)
|        at RemoteCoreImpl$CoreRequest.request (RemoteCoreImpl.java:838)
|        at RemoteCoreImpl$CoreRequest.request (RemoteCoreImpl.java:829)
|        at AbstractAsyncRequest.execute (AbstractAsyncRequest.java:48)
|        at RemoteCoreImpl.request (RemoteCoreImpl.java:399)
|        at RemoteCoreImpl.publish (RemoteCoreImpl.java:3336)
|        at PgxGraph.publishAsync (PgxGraph.java:3193)
|        at PgxGraph.publishAsync (PgxGraph.java:3174)
|        ...
```

## 参考資料
 - Oracle Graph Property Graph Developer’s Guide
  ~ 3.1 PGX User Authentication and Authorization
https://docs.oracle.com/en/database/oracle/oracle-database/20/spgdg/using-inmemory-analyst-oracle-database.html#GUID-FE3E0493-D720-4215-A28C-00F03F3BE5D6

 - Oracle Labs Parallel Graph AnalytiX (PGX) 
  ~ Class PgxGraph
https://docs.oracle.com/cd/E56133_01/latest/javadocs/oracle/pgx/api/PgxGraph.html