これは InterSystems FAQ サイトの記事です。
管理ポータル > [システム管理] > [セキュリティ] 以下の設定は、%SYSネームスペースにあるSecurityパッケージ以下クラスが提供するメソッドを利用することでプログラムから作成することができます。
以下シナリオに合わせたセキュリティ設定例をご紹介します。
シナリオ:RESTアプリケーション用設定を作成する
事前準備
シナリオの中で使用するソースを2種類インポートします。
アプリケーション用RESTディスパッチクラスをインポートします。
Class Test.REST Extends %CSP.REST
{
Parameter CHARSET = "utf-8";
Parameter CONTENTTYPE = "application/json";
Parameter CONVERTINPUTSTREAM = 1;
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/hello" Method="POST" Call="TestInsert" Cors="true" />
<Route Url="/hello" Method="DELETE" Call="TestDelete" Cors="true" />
</Routes>
}
/// Description
ClassMethod TestInsert() As %Status
{
#dim %request As %CSP.Request
#dim rset As %SQL.StatementResult
set status = $$$OK
Try {
set bodyjson={}.%FromJSON(%request.Content)
set ^Test=bodyjson.Message
set stmt=##class(%SQL.Statement).%New()
$$$ThrowOnError(stmt.%Prepare("insert into Test.Human (Name,Message) VALUES(?,?)"))
set rset=stmt.%Execute($username,bodyjson.Message)
if rset.%SQLCODE<0 {
throw ##class(%Exception.SQL).CreateFromSQLCODE(rset.%SQLCODE,rset.%Message)
}
set j={}
set j.Result="こんにちは!メッセージをUSERネームスペースのグローバル^TestとTest.Humanテーブルに格納しました。"
do j.%ToJSON() }
Catch ex {
Set status=ex.AsStatus()
}
Return status
}
ClassMethod TestDelete() As %Status
{
#dim %request As %CSP.Request
#dim rset As %SQL.StatementResult
set status = $$$OK
Try{
kill ^Test
set stmt=##class(%SQL.Statement).%New()
$$$ThrowOnError(stmt.%Prepare("Delete from Test.Human"))
set rset=stmt.%Execute()
if rset.%SQLCODE<0 {
throw ##class(%Exception.SQL).CreateFromSQLCODE(rset.%SQLCODE,rset.%Message)
}
set j={}
set j.Result="USERネームスペースの^TestとTest.Humnaのデータを削除しました"
do j.%ToJSON()
}
catch ex {
Set status=ex.AsStatus()
}
Return status
}
}
ObjectScriptObjectScript
続いて、Test.Humanテーブル用定義もインポートします。
Class Test.Human Extends %Persistent
{
Property Name As %String;
Property Message As %String;
}
ObjectScriptObjectScript
それでは設定してみましょう。
1) RESTアプリケーション(/testApp)があり、USERネームスペースで動作するRESTアプリケーションに対して不特定多数のユーザが利用できるようにします=(認証なしアクセスを許可します)。
2) RESTアプリケーション利用時は、USERネームスペースのTestスキーマ以下テーブルに対してINSERT/UPDATE/DELETE/SELECTが行えるように設定します。
まずは 1)について、
認証をしない「認証なし」アクセスを許可した場合、UnknownUserとしてInterSystems製品にログインします。
UnknownUserはインストール時の初期セキュリティの指定により初期設定が異なります。(初期セキュリティについて詳細は、記事「インストール時の初期セキュリティについて」をご参照ください。)
- 「最小」:InterSystems製品の全ての情報にアクセス可能な %Allロールが付与されます。
- 「通常」以上を指定した場合:ロールは何も設定されません。
このシナリオでは、UnkownUserに対してロールが付与されていない環境(=インストール時の初期セキュリティを「通常」以上とした場合)に対する設定方法を解説します。
手っ取り早くUnkownUserに%Allロールを付与するのも1つの方法ですが、その場合RESTアプリケーション以外の「認証なし」が許可されたアクセスに対しても%Allロールが適用されてしまうため、セキュアな設定とは言えません。このシナリオでは、RESTアプリケーションパスを通過したときのみ適切なロールを付与させることのできる「アプリケーションロール」を利用してロールを付与していきます。
ここで、RESTアプリケーションを動作させるために最低限必要となるアクセス許可はアプリケーションが動作するデータベースに対するREAD許可です。アプリケーションがデータベースに対して書き込みを行う場合はWRITE許可も必要となります。
InterSystems製品では、データベースを作成した際、一緒にセキュリティ設定で使用するデータベースリソースを作成することができます。データベースリソースを作成するとそのリソースに対するREADとWRITEの許可を持ったデータベースロールが自動的に作成されます。
今回はインストールデフォルトで作成されるUSERデータベースにアプリケーションをインストールして利用することにしています。
USERデータベースに対しては、%DB_USERリソースが用意されていてこのリソースに対してREADとWRITE許可を持つ%DB_USERロールが事前に用意されています。この%DB_USERロールをRESTアプリケーションに付与することにします。
さらに、2)では、
2) RESTアプリケーション利用時は、USERネームスペースのTestスキーマ以下テーブルに対してINSERT/UPDATE/DELETE/SELECTが行えるように設定します。
とあるので、Testスキーマに対する適切なテーブル権限が必要となります。テーブル権限はユーザに直接付与することもロールに付与することもできます。
この設定では、RESTアプリケーションにロールを付与したいので、ロールにテーブルの権限も付与することにします。
それでは、ロール:MyAppRoleを作成します。
アプリケーションはUSERネームスペースにアクセスする前提のため、%DB_USERロールを持つ新ロールを作成します。
%SYSネームスペースで実行します。
set $namespace="%SYS"
set status=##class(Security.Roles).Create("MyAppRole","アプリケーション用ロール",,"%DB_USER")
ObjectScriptObjectScript
Security.RolesクラスのCreate()メソッドに指定する引数は以下の通りです。
第1引数:ロール名
第2引数:ロールの説明
第3引数:リソースの割り当て(未指定もOK)
第4引数:割り当てるロール(複数ある場合はカンマ区切りで指定)
このロールを付与された人やアプリケーションはTestスキーマに対してSELECT/DELETE/UPDATE/INSERTができるようにこれらの権限を含む全テーブル権限を追加します。
set status=$system.SQL.Security.GrantPrivilege("*","Test","SCHEMA","MyAppRole")
ObjectScriptObjectScript
%SYSTEM.SQL.SecurityクラスのGrantPrivilege()メソッドで指定する引数は以下の通りです。
第1引数:以下のアクションをカンマ区切りで指定します。全部対象とする場合は * を指定します。
- Alter
- Select
- Insert
- Update
- Delete
- References
- Execute
- Use
第2引数:対象となるテーブル名やスキーマ名を指定できます。例ではTestスキーマを指定しています。
第3引数:対象となるタイプを指定します。例ではSCHEMAを指定しています。
第4引数:付与するユーザ名またはロール名を指定します。
戻り値を確認します。(1が返れば成功です。)
失敗している場合は、以下の出力をご確認ください。
do $system.OBJ.DisplayError(status)
ObjectScriptObjectScript
管理ポータルでは以下のように設定を確認できます。
管理ポータル > [システム管理] > [セキュリティ] > [ロール] > [MyAppRole]の[Assigned To]と[SQL Tables]のタブ
作成したMyAppRoleをアプリケーション利用時に追加するようにREST用のウェブアプリケーションを定義します。
作成には、Security.ApplicationsクラスのCreate()メソッドを利用します。
Create()メソッドの第2引数には設定に必要な情報を配列変数で指定します。サブスクリプトの指定については以下の通りです。
- DispatchClass:RESTディスパッチクラス名を指定します。
- NameSpace:RESTディスパッチクラスがあるネームスペースを指定します。
- Enable:アプリケーションを有効とする場合は1を指定します。
- AutheEnabled:認証なしは64を設定します。詳細はAutheEnabledプロパティの説明をご参照ください。(認証なしは Bit 6のAutheUnauthenticated の値を設定します。)
- MatchRoles:アプリケーション通過時に付与するアプリケーションロールの場合は、:ロール名 を設定します。
%SYSネームスペースで以下実行します。
set webName="/testApp"
set webProperties("DispatchClass")="Test.REST"
set webProperties("NameSpace")="USER"
set webProperties("Enable")=1
set webProperties("AutheEnabled")=64
set webProperties("MatchRoles")=":MyAppRole"
set status=##class(Security.Applications).Create(webName, .webProperties)
ObjectScriptObjectScript
戻り値を確認します。(1が返れば成功です。)
失敗している場合は、以下の出力をご確認ください。
do $system.OBJ.DisplayError(status)
ObjectScriptObjectScript
管理ポータルでは以下のように表示されます。
管理ポータル > [システム管理] > [セキュリティ] > [アプリケーション] > [ウェブ・アプリケーション] > /testApp選択
設定が完了したので最初にPOST要求を試します。 URLには、webサーバ/testApp/hello を指定しBodyに以下のプロパティを持つJSONを指定しPOST要求をテストします。
{
"Message":"新しいデータをいれます"
}
JSONJSON
POST要求が成功すると、以下応答として返送します。
{
"Result": "こんにちは!メッセージをUSERネームスペースのグローバル^TestとTest.Humanテーブルに格納しました。"
}
JSONJSON
グローバル^Test、またはTest.Humanの中身をご確認ください。
続いて、DELETE要求を実行します。(POSTと同じURLを使用します)
DELETE要求が成功すると、以下応答として返送します。
{
"Result": "USERネームスペースの^TestとTest.Humnaのデータを削除しました"
}
ObjectScriptObjectScript
グローバル^TestとTest.Humanテーブルのデータが削除されたことを確認してください。
ご参考:RESTアプリケーション(/testApp)にテーブル権限を設定し忘れると以下のエラーが出力されます。
{
"errors": [
{
"code": 5540,
"domain": "%ObjectErrors",
"error": "エラー #5540: SQLCODE: -99 メッセージ: User UnknownUser is not privileged for the operation",
"id": "SQLCode",
"params": [
-99,
"User UnknownUser is not privileged for the operation"
]
}
],
"summary": "エラー #5540: SQLCODE: -99 メッセージ: User UnknownUser is not privileged for the operation"
}
JSONJSON