ライセンスの予約
「30 秒も経ってるのにサービスを受けられないなんて、 あり得ない! もう結構!」
「大変申し訳ございません。 次回からはご予約なさってはいかがでしょうか。」
お気に入りのレストランでこんなコメントを聞いたら、そんな発言はばかばかしいと思うのではないでしょうか。 でも、API のコンテキストでは、まったく合理的な意見です。 お気に入りのレストランと同じように、API にも常連客がいます。よく訪問するユーザーのことです。 同じように繰り返し予約が可能であればよいと思いませんか?
これには、IRIS の基本機能がいくつか関わってきます。 まず、%SYSTEM.License インターフェースを理解する必要があります。 これは、IRIS インスタンスのライセンス使用状況に関する情報を取得するために提供されているインターフェースです。 次に、%CSP.SessionEvents クラスについて学ぶ必要があります。 このクラスを使うと、CSP セッションのライフサイクル全体で呼び出される様々なメソッドをオーバーライドできます。
まずは、%SYSTEM.License インターフェースから見ていきましょう。 このクラスには、$SYSTEM.License を使って呼び出せるメソッドもあれば、##class(%SYSTEM.License) で呼び出す必要のあるメソッドもあります。 一貫性を維持するためにも、ここでは、すべてのメソッドに使用できる ##class(%SYSTEM.License) を使用しましょう。 このユースケースでは、使用できるライセンス数と特定のユーザーが使用しているライセンス数をチェックできる必要があります。
まず、使用できるライセンス数を確認しましょう。 少なくとも 1 つのライセンスが使用可能である場合、予約には十分であるため、それ以上に確認する必要はありません。 また、ログインしようとしているユーザーが予約を保持しているユーザーであるかを確認する必要もあります。 これは、以下のように、LUAvailable メソッドへの単純な呼び出しで実現できます。
if (##class(%SYSTEM.License).LUAvailable()) || ($USERNAME = username) > 0{ return $$$OK}
次に、予約済みのユーザーが現在ログイン中であるかを確認する必要があります。 すでにレストランで着席している場合は、使用できる席があるかどうかを確認する必要はありません。 ユーザーが常に特定のアドレスから接続する場合は、LicenseCount メソッドを使用してそれを確認することができます。 ユーザー識別子を引数として取り、そのユーザーがその時点でライセンスを使用しているかどうかを示す整数を返します。 例:
set licenseusage = ##class(%SYSTEM.License).LicenseCount("David@127.0.0.1")
このユーザーがライセンスを使用しているかどうかがわかります。 0 を返す場合、そのユーザーはライセンスを使用していませんが、1 を返す場合はライセンスを使用していることになります。 このメソッドが 1 よりも大きい数値を返す場合、ユーザーが最大接続数を超過しており、接続ごとに 1 つのライセンスを使用していることを意味するため、注意が必要です。 その場合は、別の問題を新たに修正する必要があります!
どのアドレスから接続されているのかがわからない場合は、この状況に別の方法で対応して、いくつかの追加ステップが必要となります。 ここでは、ConnectionList クラスのクエリを使用しましょう。 次のコードを検討してください。
ClassMethod CheckReservation(username As %String) As %Status{ try{ if (##class(%SYSTEM.License).LUAvailable() > 1) || ($USERNAME = username) { return $$$OK } set rs = ##class(%ResultSet).%New("%SYSTEM.License:ConnectionList") set sc = rs.%Execute() if $$$ISERR(sc) {$$$ThrowStatus(sc)} while rs.%Next(){ if $P(rs.%GetData(1),"@",1) = username{ return $$$OK } } $$$ThrowStatus($$$ERROR(5001,"Reserved User Not Logged In.")) } catch ex{ do ex.Log() return ex.AsStatus() }}
このメソッドでは、ユーザー名に基づいて 1 つのライセンスのみを予約できることに注意してください。 ユースケースで 2 つ以上のライセンスを確保する必要がある場合は、ConnectionList クエリの結果を確認し、何人の顧客がログイン済みであり、席を待つ間にバーで最初のグラスを傾けているかを数える必要があります。 次に、残りの LUAvailable が単に 2 以上であるだけでなく、すべての予約に対応できるかを確認する必要があります。
このメソッドは通常、%ResultSet オブジェクトを使用して、%SYSTEM.License クラスから LicenseCount クエリにアクセスします。 一般に %SQL.Statement を使用する方が好ましくはありますが、%PrepareClassQuery は必ずこのクエリで失敗します。 そのため、ここではこれを使用することはできません。 このクエリは各接続に対して行を返すため、その後で結果セットを反復しましょう。 各行に対し、最初の列には、ユーザー名と IP アドレスを@ で区切ったライセンス ID が含まれます。 探しているのは(ユーザー名と IP アドレスの組み合わせではなく)ユーザー名のみであるため、その列の最初の部分を提供されたユーザー名と比較して、一致する場合は成功ステータスを返します。 ただし、結果セット全体にユーザー名が見つからない場合は、エラーを返します。
このメソッドを、代わりにロールをチェックするように変更したい場合(スーパーユーザーがいつでもシステムにアクセスできるように、%All ロールを持つ少なくとも 1 人のユーザーがログインできるようにするなど)、%SYS ネームスペースに切り替えて、ループを反復する過程で各ユーザーのロールを検証することで実現できます。 ##class(Security.Users).Get($P(rs.%GetData(1),”@”,1),.props) を使用してから、props(“Roles”) [ “%All” を使用して、ロールが props(“Roles”) に含まれているかを確認できます。 正しいアプリケーションネームスペースにログインできるように、最終的には元のネームスペースに忘れずに戻るようにしましょう。 すると、コードは以下のようになります。
ClassMethod CheckReservation(role As %String) As %Status{ try{ set returnns = $NAMESPACE zn "%SYS" set sc = ##class(Security.Users).Get($USERNAME,.props) if (##class(%SYSTEM.License).LUAvailable() > 1) || (props("Roles") [ role) { zn returnns return $$$OK } set rs = ##class(%ResultSet).%New("%SYSTEM.License:ConnectionList") set sc = rs.%Execute() if $$$ISERR(sc) {$$$ThrowStatus(sc)} while rs.%Next(){ set sc = ##class(Security.Users).Get($P(rs.%GetData(1),"@",1),.props) if props("Roles") [ role{ zn returnns return $$$OK } } zn returnns $$$ThrowStatus($$$ERROR(5001,"Reserved User Not Logged In.")) } catch ex{ do ex.Log() return ex.AsStatus() }}
License API 内には、使用できる実用的なクラスクエリが他にもあります。 特定のアプリケーションへのログインを検証する場合は、ConnectionAppList の方が適しているかもしれません。 ライセンスのタイプ(User、CSP、Mixed、または Grace)を検査する場合は、UserList クラスクエリを使用すると良いでしょう。 ProcessList クエリを操作すると、ユーザーが実行するプロセスの詳細をさらに詳しく把握することができます。 ただし、あまりにも夢中になりすぎた場合は、ログインプロセスにゆっくりと取り組むようにしましょう。
この部分のコードが完成したので、どこに配置するかを決めなければなりません。 ただし、きちんと配置するには、%CSP.SessionEvents クラスの基本を知っておく必要があります。 このクラスには、CSP セッションが開始、終了、またはタイムアウトになった場合やユーザーがログインまたはログアウトした場合の動作を制御するためにオーバーライドできるメソッドがいくつか含まれています。 ここでは、セッションに割り当てられたライセンスの前に実行する OnStartSession() メソッドに特に注目できます。 そこでメソッドを実行し、エラーを返した場合には、%session.EndSession を 1 にセットするだけで完了です。 すると、ユーザーには標準のライセンス数不足メッセージが表示され、ログインできないようにすることができます。
%CSP.SessionEvents 拡張機能と以前に定義したクラスメソッドを使って、User.SessionEvents というメソッドを作成します。 次に、その中の 1 つのメソッドのみをオーバーライドし、他の予約ようにスペースを空けておくようにします。 すると、そのメソッドは次のようになります。
ClassMethod OnStartSession(){ if $$$ISERR(##class(User.SessionEvents).CheckReservation("David")){ set %session.EndSession=1 } Quit $$$OK}
ネームスペースが複数ある場合は、このクラスを使用するすべての CSP アプリケーションにクラスを作成するようにしてください。
最後に、アプリケーションがこれらのセッションを使用できるように構成します。 これは、管理ポータルで、システム管理 > セキュリティ > アプリケーション > Web アプリケーションに移動して行えます。 カスタムセッションイベントクラスを使用するアプリケーションをクリックし、 次に、「イベントクラス」ボックスに User.SessionEvents を入力します。 それ以降、ログインするたびに、カスタマイズされた OnStartSession メソッドが呼び出され、予約の余地がない場合には他のユーザーがログインできなくなります。
ここには、検討すべき重要な実装の詳細が 1 つあります。 これらのセッションイベントをシステム管理ポータルに適用すべきかどうかです。 適用する場合は、何らかの問題が生じても、ログインして修正できない状況に陥る可能性があります。 適用しない場合は、誰かがログインし、予約しようとしていたライセンスを使用してしまう可能性があります。 おそらく、そのユーザーに予約チェックを迂回できる特定のロールがあるかを確認するようにコードを編集することができますが、 これについては、あなた次第です。
今後は、常連客が定期的に訪れるたびに、席に座れることを保証できます!