2025年6月6日金曜日

Oracle REST Data ServicesのRESTサービスを保護する方法を確認する

Oracle REST Data Servicesの以下のドキュメントに記載されている、RESTサービスを保護する方法について確認してみました。

Oracle REST Data Services Developer's Guide Release 25.1

以下の構成による保護について、動作を確認しています。
  1. ORDSが管理するファイル・ベースのユーザー作成と、そのユーザーによるBasic認証による保護。
  2. データベースに作成したユーザーと、そのユーザによるOAuth2.0のクライアントクレデンシャル認証(Client Credentials Grant)による保護。
  3. データベースに作成したユーザーと、そのユーザによるOAuth2.0の認可コード認証(Authorization Code Grant)による保護。
ドキュメントにはその他、Implicit Grantによる保護の手順も説明されています。しかしImplicit GrantはOAuth2.1からは削除される予定です。そのため動作確認はしません。

The OAuth 2.1 Authorization Framework
10.1. Removal of the OAuth 2.0 Implicit grant

動作確認はしましたが、ファイル・ベースのユーザー管理は実用的ではなく、またAutonomous Databaseでは(顧客管理ORDSであれば可能)実装できません。認可コード認証についても同じく、認可コードの取得時にファイル・ベースのユーザーによるBasic認証が必要なので、同じ制限があります。認可コード認証についてはPKCEにも対応していません。

動作検証を行った結果として、以下の2点が言えます。
  1. サーバーからORDSのRESTサービスを呼び出すときは、クライアントクレデンシャル認証が使えます。
  2. クライアントの権限でORDSのRESTサービスを呼び出すときは、JWTプロファイルを使うことになります。JWTプロファイルについては、同じDeveloper's Guideの2.9 JWT Profile and JWT Profile RBACに記載されています。
JWT プロファイルについては以前に記事「ORDS 23.3で追加されたOAUTH.CREATE_JWT_PROFILEを使ってRESTサービスを保護する」で使い方を紹介しています。ORDS 25.1では、ORDS_SECURITY.CREATE_JWT_PROFILEプロシージャに引数p_role_claim_nameが追加され、スコープ(ORDSの権限)に加えてロールも活用できるように機能拡張されました。

Oracle REST Data ServicesのSenior Product ManagerのChris Hoinaさんが、オラクル社の公式ブログにJWTプロファイルとロールの使い方について記事を書かれています。以下の記事ではIAMのユーザーに割り当てたグループを、ORDSのロールとして認識させる手順を紹介しています。

How to Secure Oracle Database REST APIs with OCI IAM (IDCS) JSON Web Tokens and Role-Based Access Claims, Part One
How to Secure Oracle Database REST APIs with OCI IAM (IDCS) JSON Web Tokens and Role-Based Access Claims, Part Two

パッケージOAUTHはORDS_SECURTY、OAUTH_ADMINはORDS_SECURITY_ADMINに置き換えられる予定です。ORDS_SECURITY.CREATE_JWT_PROFILEに引数p_role_claim_nameは含まれていますが、ドキュメントは更新されていません。引数p_role_claim_nameの説明は、OAUTH.CREATE_JWT_PROFILEにあります。

以下より実際に行った動作確認の作業を紹介します。作業を行った環境はローカルのmacOSのpodman上に作成したOracle APEXの環境です(作成手順についてはこちらの記事を参照のこと)。ORDSはコンテナapex-ordsで実行されています。Oracle APEXが実装されている環境ですが、APEXは使用しません。


RESTサービスの準備



最初にRESTサービスを実装するデータベース・ユーザーとしてORDSTESTを作成します。データベースにSYSで接続して、以下のコマンドを実行します。データベースはOracle Database 23ai Freeを想定しているため、デフォルト表領域はUSERS、一時表領域はTEMPになります。
create user ordstest identified by [パスワード] default tablespace users temporary tablespace temp;
grant db_developer_role to ordstest;
alter user ordstest quota unlimited on users;

% sql sys/********@localhost/freepdb1 as sysdba



SQLcl: 金 6月 06 10:33:32 2025のリリース24.4 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


接続先:

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01


SQL> create user ordstest identified by ********* default tablespace users temporary tablespace temp;


User ORDSTESTは作成されました。


SQL> grant db_developer_role to ordstest;


Grantが正常に実行されました。


SQL> alter user ordstest quota unlimited on users;


User ORDSTESTが変更されました。


SQL> exit

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01から切断されました

% 


これより先はデータベースにユーザーORDSTESTで接続し、作業を進めます。

動作確認のために呼び出すRESTサービスを作成します。以下のスクリプトをユーザーORDSTESTで実行します。モジュールexample、テンプレートcurrentuser、そのテンプレートにGETハンドラを作成します。

以下のSELECT文を実行し、作成されたRESTサービスを確認します。
select id, name, uri_prefix from user_ords_modules where name = 'example';
select id, module_id, uri_template from user_ords_templates where uri_template = 'currentuser';
select id, template_id, source_type, method, source from user_ords_handlers where template_id = (select id from user_ords_templates where uri_template = 'currentuser');

% sql ordstest/********@localhost/freepdb1



SQLcl: 金 6月 06 10:40:49 2025のリリース24.4 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


接続先:

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01


SQL> @sample-rest-service.sql


PL/SQLプロシージャが正常に完了しました。


SQL> select id, name, uri_prefix from user_ords_modules where name = 'example';


      ID NAME       URI_PREFIX    

________ __________ _____________ 

   10083 example    /example/     


SQL> select id, module_id, uri_template from user_ords_templates where uri_template = 'currentuser';


      ID    MODULE_ID URI_TEMPLATE    

________ ____________ _______________ 

   10084        10083 currentuser     


SQL> select id, template_id, source_type, method, source from user_ords_handlers where template_id = (select id from user_ords_templates where uri_template = 'currentuser');


      ID    TEMPLATE_ID SOURCE_TYPE    METHOD    SOURCE                                              

________ ______________ ______________ _________ ___________________________________________________ 

   10085          10084 plsql/block    GET       declare

    l_response json_object_t := json_object_t();

    l_response_clob clo    


SQL> exit

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01から切断されました

% 


作成したRESTサービスを呼び出します。curlコマンドを使用します。呼び出されるRESTサービスは、ORDSのハンドラ内でバインド変数:current_userとして取得できる値と受信したAuthorizationヘッダーを出力します。

curl -i http://localhost:8181/ords/ordstest/example/currentuser

現時点ではRESTサービスを保護していないため、current_user、Authorizationヘッダーともに値は取得されません。

{"current_user":"no user","Authorization":"no authorization header"}

% curl -i http://localhost:8181/ords/ordstest/example/currentuser

HTTP/1.1 200 OK

Content-Type: text/html;charset=utf-8

ETag: "sWHiIhReqYdcWo/KOFXgxgndWr0bbgjZZt1KMGxnROqzVE20Mk07XBBqmORReJhN+iglTG3wP6WiOBEAujonEw=="

Transfer-Encoding: chunked


{"current_user":"no user","Authorization":"no authorization header"}

% 


ロールTester Role、権限examle.testerを作成し、先ほど作成したRESTサービス・モジュールexampleを保護します。以下のスクリプトを実行します。

以下のSELECT文を実行し、RESTサービスに適用した保護を確認します。
select id, label, name from user_ords_privileges where name = 'example.tester';
select module_id, module_name, privilege_name from user_ords_privilege_modules where privilege_name = 'example.tester';
select id, name from user_ords_roles where name = 'Tester Role';
select privilege_id, privilege_name, role_id, role_name from user_ords_privilege_roles where privilege_name = 'example.tester';

% sql ordstest/*********@localhost/freepdb1



SQLcl: 金 6月 06 10:57:18 2025のリリース24.4 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


接続先:

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01


SQL> @sample-rest-protection.sql


PL/SQLプロシージャが正常に完了しました。


SQL> select id, label, name from user_ords_privileges where name = 'example.tester';


      ID LABEL           NAME              

________ _______________ _________________ 

   10087 Current User    example.tester    


SQL> select module_id, module_name, privilege_name from user_ords_privilege_modules where privilege_name = 'example.tester';


   MODULE_ID MODULE_NAME    PRIVILEGE_NAME    

____________ ______________ _________________ 

       10083 example        example.tester    


SQL> select id, name from user_ords_roles where name = 'Tester Role';


      ID NAME           

________ ______________ 

   10086 Tester Role    


SQL> select privilege_id, privilege_name, role_id, role_name from user_ords_privilege_roles where privilege_name = 'example.tester';


   PRIVILEGE_ID PRIVILEGE_NAME       ROLE_ID ROLE_NAME      

_______________ _________________ __________ ______________ 

          10087 example.tester         10086 Tester Role    


SQL> exit

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01から切断されました

% 


curlでRESTサービスを呼び出し、RESTサービスが保護されていることを確認します。

curl -i http://localhost:8181/ords/ordstest/example/currentuser

レスポンスとして401 Unauthorizedが返されます。

% curl -i http://localhost:8181/ords/ordstest/example/currentuser


HTTP/1.1 401 Unauthorized

Content-Type: application/problem+json

Content-Length: 182


{

    "code": "Unauthorized",

    "message": "Unauthorized",

    "type": "tag:oracle.com,2020:error/Unauthorized",

    "instance": "tag:oracle.com,2020:ecid/oFO6DMKBX6mqmmF-SMe6kw"

}

% 



ORDSに作成したユーザーによるBasic認証



Oracle REST Data Servicesにユーザーtesterを作成します。このユーザーはORDSによるBasic認証に使用されます。ORDSによって認証されたユーザーtesterにロールTester Roleを割り当てることにより、保護されたRESTモジュールへのアクセスを認可します。

今回の環境ではORDSはコンテナapex-ordsで動作しているため、コンテナに接続してからユーザーtesterを作成しています。また、ユーザーを追加した後にORDSを再起動する必要があるため、コンテナapex-ordsごと再起動しています。

podman exec -it apex-ords bash
ords --config /etc/ords/config config user add tester "Tester Role"
exit
podman restart apex-ords


% podman exec -it apex-ords bash

[oracle@apex ords]$ ords --config /etc/ords/config config user add tester "Tester Role"


ORDS: Release 25.1 Production on Fri Jun 06 02:12:22 2025


Copyright (c) 2010, 2025, Oracle.


Configuration:

  /etc/ords/config


Enter the password for tester: ********

Confirm password: ********

Created user tester in file /etc/ords/config/global/credentials

[oracle@apex ords]$ exit

exit

% podman restart apex-ords

apex-ords

% 


ユーザーtesterの情報はファイル/etc/ords/config/global/credentialsに保存されています。

[oracle@apex ords]$ cat /etc/ords/config/global/credentials 

tester;{SSHA-512}R7cATDYV6Zabh6ryiS/2tqGS72ETJqdzF+Ubtg1oXINmnSUzErakhYe964PwWvPGHNIa05bUSybpyjB+PfbfPs5WPNY/+bzS;Tester Role

[oracle@apex ords]$ 


ユーザーtesterを指定してRESTサービスを呼び出します。

curl -i -u tester:[パスワード] http://localhost:8181/ords/ordstest/example/currentuser

レスポンスとして以下が返され、ユーザーtesterで認証されてRESTサービスにアクセスできていることが確認できます。

{"current_user":"tester","Authorization":"Basic dGVzdGVyOnRlc3Rlcg=="}

% curl -i -u tester:****** http://localhost:8181/ords/ordstest/example/currentuser

HTTP/1.1 200 OK

Content-Type: text/html;charset=utf-8

ETag: "8IE0ooG0h49SrS5g6HYfKP2+cV9S9mrqllYnbDo6LPXdDgKCVPiPmyO6aIJemUjPHpytKt+/OX1KvPTFeWcOWA=="

Transfer-Encoding: chunked


{"current_user":"tester","Authorization":"Basic dGVzdGVyOnRlc3Rlcg=="}

% 



OAuth2.0のクライアントクレデンシャル認証



OAuthクライアントClient Credentials Exampleを作成し、OAuth2.0のクライアントクレデンシャル認証を行いアクセス・トークンを取得して、RESTサービスにアクセスします。

以下のスクリプトを実行し、OAuthクライアントClient Credentials Exampleの作成とロールTester Roleの割り当てを行います。


clilent_idclient_secretが印刷されるので、それらをコピーします。client_secretは1度しか印刷されません。コピーを忘れた場合は再度スクリプトを実行して、新たにclient_secretを生成します。

% sql ordstest/********@localhost/freepdb1



SQLcl: 金 6月 06 13:24:46 2025のリリース24.4 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


接続先:

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01


SQL> @create-oauth-client-client-credentials.sql

Client Credentials Example created.

client_id: 9A0HhMMUt1YVrMm_U_qq_Q..

client_secret: c6aagkaaZwYoyygPjP7SXA..



PL/SQLプロシージャが正常に完了しました。


SQL> 


作成したOAuthクライアントClient Credentials ExampleとロールTester Roleの割り当てを、以下のSELECT文を実行して確認します。
select id, name, client_id, client_secret from user_ords_clients where name = 'Client Credentials Example';
select * from user_ords_client_roles where client_name = 'Client Credentials Example';
パッケージORDS_SECURITYのプロシージャによりOAuthクライアントを作成した場合、ビューUSER_ORDS_CLIENTSの列CLIENT_SECRETにシークレットは表示されません。
 

SQL> select id, name, client_id, client_secret from user_ords_clients where name = 'Client Credentials Example';


      ID NAME                          CLIENT_ID                   CLIENT_SECRET    

________ _____________________________ ___________________________ ________________ 

   10150 Client Credentials Example    9A0HhMMUt1YVrMm_U_qq_Q..    ********         


SQL> 

SQL> select * from user_ords_client_roles where client_name = 'Client Credentials Example';


   CLIENT_ID CLIENT_NAME                      ROLE_ID ROLE_NAME      

____________ _____________________________ __________ ______________ 

       10150 Client Credentials Example         10086 Tester Role    


SQL> 

SQL> exit

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01から切断されました

% 


ORDSが提供しているトークン生成のURLを呼び出し、アクセス・トークンを取得します。Basic認証のユーザー名としてclient_id、パスワードとしてclient_secretを与えます。

curl -i -u client_id:client_secret --data "grant_type=client_credentials" http://localhost:8181/ords/ordstest/oauth/token

トークンURLからaccess_tokenが返されます。

{"access_token":"qjzIbIY64O-pQ8yP7z0aZA","token_type":"bearer","expires_in":3600}

% curl -i -u 9A0HhMMUt1YVrMm_U_qq_Q..:c6aagkaaZwYoyygPjP7SXA.. --data "grant_type=client_credentials" http://localhost:8181/ords/ordstest/oauth/token

HTTP/1.1 200 OK

Content-Type: application/json

Cache-Control: no-cache, no-store, max-age=0

X-Frame-Options: SAMEORIGIN

Transfer-Encoding: chunked


{"access_token":"qjzIbIY64O-pQ8yP7z0aZA","token_type":"bearer","expires_in":3600}

% 


このアクセス・トークンをAuthorizationヘッダーに設定し、RESTサービスを呼び出します。

curl -i -H "Authorization: Bearer アクセス・トークン" http://localhost:8181/ords/ordstest/example/currentuser

OAuthクライアントClient Credentials Exampleで認証され、クライアントにTester Roleが割り当てられているため、RESTサービスのアクセスが認可されレスポンスが返されます。

ただし、バインド変数:current_userにはOAuthクライアントの名前ではなくclient_idが設定されています。

% curl -i -H "Authorization: Bearer qjzIbIY64O-pQ8yP7z0aZA" http://localhost:8181/ords/ordstest/example/currentuser

HTTP/1.1 200 OK

Content-Type: text/html;charset=utf-8

ETag: "oInXCooX/Bu9/E2p9OjzaYAoK50gwin/oP6DQMY/+0C1KETn8aNhqlGBa7fVbQ1M9WWCXkMHBM0004OQZIsMBA=="

Transfer-Encoding: chunked


{"current_user":"9A0HhMMUt1YVrMm_U_qq_Q..","Authorization":"Bearer qjzIbIY64O-pQ8yP7z0aZA"}

% 



OAuth2.0の認可コード認証



認可コード認証の動作確認については、ORACLE-BASEの以下の記事に多くを拠っています。

Oracle REST Data Services (ORDS) : Authentication

OAuthクライアントAuthorization Code Exampleを作成し、OAuth2.0の認可コード認証を行いアクセス・トークンを取得して、RESTサービスにアクセスします。

以下のスクリプトを実行し、OAuthクライアントAuthorization Code Exampleの作成とロールTester Roleの割り当てを行います。


client_idclient_secretが印刷されるので、それらをコピーします。client_secretは1度しか印刷されません。

% sql ordstest/********@localhost/freepdb1



SQLcl: 金 6月 06 13:49:07 2025のリリース24.4 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


接続先:

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01


SQL> @create-oauth-client-client-authorization-code.sql 

Authorization Code Example created.

client_id: kw-gSKLOc6dGeOYZn8eT3Q..

client_secret: It8zNj0WuVloOESohhfO4A..



PL/SQLプロシージャが正常に完了しました。


SQL> 


作成したOAuthクライアントAuthorization Code ExampleとロールTester Roleの割り当てを、以下のSELECT文を実行して確認します。
select id, name, client_id, client_secret from user_ords_clients where name = 'Authorization Code Example';
select * from user_ords_client_roles where client_name = 'Authorization Code Example';
パッケージORDS_SECURITYのプロシージャによりOAuthクライアントを作成した場合、ビューUSER_ORDS_CLIENTSの列CLIENT_SECRETにシークレットは表示されません。

SQL> select id, name, client_id, client_secret from user_ords_clients where name = 'Authorization Code Example';


      ID NAME                          CLIENT_ID                   CLIENT_SECRET    

________ _____________________________ ___________________________ ________________ 

   10157 Authorization Code Example    kw-gSKLOc6dGeOYZn8eT3Q..    ********         


SQL> select * from user_ords_client_roles where client_name = 'Authorization Code Example';


   CLIENT_ID CLIENT_NAME                      ROLE_ID ROLE_NAME      

____________ _____________________________ __________ ______________ 

       10157 Authorization Code Example         10086 Tester Role    


SQL> exit

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.7.0.25.01から切断されました

% 


認可コードを生成するURLをブラウザに入力します。client_idにOAuthクライアント作成時に表示されたclient_idの値、stateには任意の文字列を指定します。

http://localhost:8181/ords/ordstest/oauth/auth?response_type=code&client_id=[client_id]&state=[任意の文字列]

401 Unauthorizedが返されます。Sign inをクリックします。


コードを生成するURLにアクセスするため、Basic認証で使用したユーザー名、パスワードを与えます。今回の作業ではtesterです。


アクセス権限をリクエストするページが開きます。承認をクリックします。


Not Foundが返されます。

これはOAuthクライアントAuthorization Code Examplep_redirect_urlとしてhttp://localhost:8181/ords/ordstest/redirectという、サービスを設定していないURLを与えているためです。


リダイレクト先のURLがブラウザの入力フィールドに、以下のように現れています。リダイレクト先に引数codeとして渡されている値が認可コードになります。

http://localhost:8181/ords/ordstest/redirect?code=NUBFF2QxcCY6oOqq8r7OUw&state=aef2jf3219

OAuthクライアントAuthorization Code Exampleclient_idclient_secret、および上記のcodeをORDSのトークンURLを呼び出しに与えて、アクセス・トークンを取得します。

curl -i -u client_id:client_secret --data "grant_type=authorization_code&code=コード" http://localhost:8181/ords/ordstest/oauth/token

トークンURLからaccess_tokenが返されます。

{"access_token":"SQn8Ad0cVGdF7svoRTmu5Q","token_type":"bearer","expires_in":3600,"refresh_token":"lFk6RZoyBZ_qmlDQP19_Yg"}

% curl -i -u kw-gSKLOc6dGeOYZn8eT3Q..:It8zNj0WuVloOESohhfO4A.. --data "grant_type=authorization_code&code=NUBFF2QxcCY6oOqq8r7OUw" http://localhost:8181/ords/ordstest/oauth/token

HTTP/1.1 200 OK

Content-Type: application/json

Cache-Control: no-cache, no-store, max-age=0

X-Frame-Options: SAMEORIGIN

Transfer-Encoding: chunked


{"access_token":"SQn8Ad0cVGdF7svoRTmu5Q","token_type":"bearer","expires_in":3600,"refresh_token":"lFk6RZoyBZ_qmlDQP19_Yg"}

%


このアクセス・トークンをAuthorizationヘッダーに設定し、RESTサービスを呼び出します。

curl -i -H "Authorization: Bearer アクセス・トークン" http://localhost:8181/ords/ordstest/example/currentuser

OAuthクライアントAuthorization Code Exampleで認証されクライアントにTester Roleが割り当てられているため、RESTサービスへのアクセスが認可されレスポンスが返されます。

バインド変数:current_userにはOAuthクライアントの名前ではなく、コード取得時に認証したBasic認証のユーザー名testerが設定されています。

% curl -i -H "Authorization: Bearer SQn8Ad0cVGdF7svoRTmu5Q" http://localhost:8181/ords/ordstest/example/currentuser

HTTP/1.1 200 OK

Content-Type: text/html;charset=utf-8

ETag: "2blAi7qSZV56Q9U+bRlQ5yeBFsanlDchMsvpinigKBYkrJfVxi4WPiGRMbhYycU4hLkIdjqD/l0afWDywJNUXA=="

Transfer-Encoding: chunked


{"current_user":"tester","Authorization":"Bearer SQn8Ad0cVGdF7svoRTmu5Q"}

%


以上で、ORDSのユーザーによるBasic認証、OAuth2.0のクライアントクレデンシャル認証および認可コード認証の動作確認は完了です。

2025年6月4日水曜日

BLOBとBase64エンコードしたCLOBの相互変換をPL/SQLとMLE JavaScriptで実行する

主にネットワーク上でバイナリ・データを送受信するにあたって、Base64でエンコードまたはデコードが必要な場合があります。Oracle APEXのサーバー・サイドのプロセスでの実行を想定して、PL/SQLとMLE JavaScriptで、BLOBからBase64のCLOBおよびその反対のBase64のCLOBからBLOBへ変換するコードを書いてみました。

それらのプロセスを組み込んだアプリケーションの画面です。


このAPEXアプリケーションのエクスポートを以下に置いています。
https://github.com/ujnak/apexapps/blob/master/exports/blob-handling.zip

機能はすべてホーム・ページに実装しています。

タイプファイルのアップロードであるページ・アイテムP1_FILEを作成しています。このページ・アイテムのストレージタイプ表APEX_APPLICATION_TEMP_FILESを選択することで、ボタンSUBMITをクリックしたときに実行されるプロセス内で、アップロードされたファイルをAPEX_APPLICATION_TEMP_FILESの列BLOB_CONTENTより、BLOBとして取り出せます。


ボタンSUBMITをクリックしたときに、以下の4つのプロセスを実行します(最初のプロセスInitializeはコレクションの初期化を行い、BLOBの変換処理は行いません)。
  • BLOB2BASE64_PLSQL - PL/SQLでBLOBをBase64エンコードしたCLOBへ変換します。結果をAPEXコレクションに保存します。
  • BLOB2BASE64_JS - JavaScriptでBLOBをBase64エンコードしたCLOBへ変換します。結果をAPEXコレクションに保存します。
  • BASE642BLOB_PLSQL - JavaScriptで変換したBase64のCLOBを、PL/SQLでBLOBへ変換します。結果をAPEXコレクションに保存します。
  • BASE642BLOB_JS - PL/SQLで変換したBase64のCLOBを、JavaScriptでBLOBへ変換します。結果をAPEXコレクションに保存します。
変換結果はすべてAPEXコレクションに保存されます。変換結果を確認するために、以下のSELECT文をソースとしたクラシック・レポートを作成しています。
select
    seq_id,
    c001,
    substr(clob001,1,60) clob001,
    dbms_lob.getlength(blob001) blob001
from apex_collections
where collection_name = :P1_COLLECTION_NAME
CLOBについては先頭60バイトが一致していれば、JavaScriptとPL/SQLの双方の変換で同じ、BLOBについては全体の長さが一致していればJavaScriptとPL/SQLの双方の変換で同じだろうと見做しています。より厳密さを求めるのであれば、DBMS_CRYPTO.HASHの利用も考慮する必要があります。パッケージDBMS_CRYPTOはデフォルトでは実行権限がなく、管理者によるGRANT文の実行が必要なためレポートに含めていません。


PL/SQLによるBLOBからBase64エンコードされたCLOBへの変換は、以下のコードで行なっています。Oracle APEXが提供しているAPEX_WEB_SERVICE.BLOB2CLOBBASE64を呼び出しています。


JavaScriptによるBLOBからBase64エンコードされたCLOBへの変換は、以下のコードで行なっています。Oracleが提供しているモジュールmle-encode-base64に含まれるファンクションencodeを呼び出しています。


PL/SQLによるBase64エンコードされたCLOBからBLOBへの変換は、以下のコードで行なっています。Oracle APEXが提供しているAPEX_WEB_SERVICE.CLOBBASE642BLOBを呼び出しています。


JavaScriptによるBase64エンコードされたCLOBからBLOBへの変換は、以下のコードで行なっています。Oracleが提供しているモジュールmle-encode-base64に含まれるファンクションdecodeを呼び出しています。

今回の記事は以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年6月3日火曜日

Oracle REST Data ServicesのRDF Graph APIを呼び出してSPARQLを実行する

Oracle Databaseは以前からRDF Graphをサポートしています。RDF Graphに関するOracle Database 23aiのドキュメントとして、以下が提供されています。

Graph Developer's Guide for RDF Graph, Release 23

このドキュメントのPart IIにRDF Graph Serverが紹介されています。しかし、RDF Graph Serverのダウンロード・ページとして以下が示されていますが、RDF GraphについてはEclipse RDF4Jのアダプタのみがダウンロード可能で、RDF Graph Serverは含まれていません。

RDF Graph Serverはどうなったのか不明だったのですが、ユーザー・インターフェース(Query UI)を除いた機能は、Oracle REST Data Servicesに実装されていました。

Oracle REST Data Services 23.4からOracle REST Data Services APIにRDF Graphが追加されています。
https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/25.1/orrst/api-rdf-graph.html

Oracle APEXを実行するにあたってOracle REST Data Servicesは必須のコンポーネントなので、APEXの環境があればSPARQLも実行できます。SPARQLからSQLへの変換はORDSで実施しているようで、オラクル・データベースでJavaVMを有効にしなくても(Autonomous DatabaseではJavaVMはデフォルトで無効です)、SPARQLを実行できます。ただし、APEXからSPARQLを実行するには、データベースからHTTPリクエストを発行してORDSを呼び出す必要があります。

今回はOracle REST Data ServicesのRDF Graph APIを呼び出すAPEXアプリケーションを作成し、SPARQLクエリを実行します。


このAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sparql-operations.zip

アプリケーションはページ・アイテムに設定した値を引数として、ボタンのクリックでREST APIを呼び出す簡単な作りになっています。

このAPEXアプリケーションをインポートすると、Web資格証明としてRDF Graph Credリモート・サーバーとしてRDF Graph ORDS Endpointが作成されます。このアプリケーション内でRDF Graph APIを呼び出す際に使用するWeb資格証明およびエンドポイントになります。


アプリケーションのインストール後にワークスペース・ユーティリティリモート・サーバーおよびWeb資格証明を開いて、これらの設定値を更新する必要があります。

リモート・サーバーRDF Graph ORDS Endpointの更新は、ワークスペース・ユーティリティリモート・サーバーから行います。


リモート・サーバーのRDF Graph ORDS Endpointを開きます。


一般エンドポイントURLを更新します。今回はAutonomous Databaseを対象にRDFグラフの保存とSPARQLの実行を行うため、エンドポイントURLは以下のようになります。

https://[Webアクセス(ORDS)パブリックURL]/[ORDS別名]/_/db-api/stable


Webアクセス(ORDS)パブリックURLは、OCIコンソールのAutonomous Databaseのツール構成から確認できます。


続けてワークスペース・ユーティリティWeb資格証明を開きます。


Web資格証明RDF Graph Credを開きます。


REST APIは、RDFグラフを保存しているスキーマ名とそのパスワードを与えることによって認証します。Autonomous DatabaseでAPEXを使用している場合は、一般にワークスペース名をAPEXDEVとした場合、ORDS別名=ワークスペース名でapexdevとなります。そのワークスペースのデフォルト・パーシング・スキーマはWKSP_APEXDEVになります。これがデータベースのユーザー名になり、このユーザー名およびパスワードWeb資格証明に設定します。


以上で、インポートしたAPEXアプリケーションを利用する準備は完了です。

このAPEXアプリケーションを使って、RDFグラフの作成とSPARQLの実行を行います。

最初にRDFネットワークを作成します。呼び出すREST APIは以下です。

Create RDF network

SEM_APIS.CREATE_RDF_NETWORKの呼び出しに対応します。

Network Nameは任意の値です。今回はNET1としています。Network Ownerは接続先のデータベース・ユーザー名(スキーマ名)を指定します。Tablespace Nameは、そのデータベース・ユーザーのデフォルト表領域を指定します。Autonomous Databaseでは表領域DATAを指定します。


ボタンCreate RDF networkをクリックすると、以下のPL/SQLコードが実行されます。REST APIの仕様にそって、API呼び出しを実施しています。レスポンスは特に加工せず、JSONのままページ・アイテムP1_RESPONSEに表示しています。



次に、作成したネットワークにモデルを作成します。Model Namebotchanとします。

ボタンCreate RDF modelをクリックすると以下のコードを実行します。呼び出すREST APIは以下になります。

Create RDF model

SEM_APIS.CREATE_RDF_GRAPHの呼び出しに対応します。



RDFネットワークとモデル(グラフ)が作成できました。SPARQLのINSERT文を実行します。

小説「坊ちゃん」の登場人物の関係データを投入します。以下のコードはClaude Sonnet 4に生成してもらいました。

ボタンExecute SPARQL Updateをクリックすると、以下のコードが実行されます。呼び出すREST APIは以下になります。

Execute a SPARQL query or update

ORDS上でSQLに変換されデータベースではSQLが実行されるため、対応するデータベースのAPIはありません。REST API呼び出し時のContent-Typeとして、application/sparql-updateを指定します。



小説「坊ちゃん」の登場人物に関するSPARQLクエリを実行してみます。以下がSPARQLクエリのサンプルです。これもClaude Sonnet 4に生成してもらいました。


この中の同盟関係の一覧を問い合わせてみます。
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX botchan: <http://example.org/botchan/>
PREFIX vocab: <http://example.org/vocabulary/>

# 同盟関係の一覧
SELECT ?person1 ?name1 ?person2 ?name2
WHERE {
  ?person1 vocab:同盟関係 ?person2 ;
           rdfs:label ?name1 .
  ?person2 rdfs:label ?name2 .
}
ボタンExecute SPARQL Queryをクリックすると、以下のコードが実行されます。REST APIのエンドポイントはUpdateと同じですが、Content-Typeとしてapplication/sparql-queryを指定しています。また、Acceptにapplication/sparql-results+jsonを指定することで、レスポンスをJSON形式で受け取っています。



野だいこと赤シャツが同盟関係にあるとのことです。

以上で、ORDSのRDF Graph APIを呼び出すことにより、RDFグラフの作成および検索ができることを確認できました。

今回の記事は以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。