セキュリティ
デフォルトでは、すべての RPC 関数は任意のクライアントから呼び出すことができ、ピア・ツー・ピア通信機能が有効になっています。どのクライアントに何を許可するかを厳密に制御するには、RpcKernelSecurity
クラスをオーバーライドできます。
import { RpcKernelSecurity, Session, RpcControllerAccess } from '@deepkit/type'; //デフォルトの実装を含みます class MyKernelSecurity extends RpcKernelSecurity { async hasControllerAccess(session: Session, controllerAccess: RpcControllerAccess): Promise<boolean> { return true; } async isAllowedToRegisterAsPeer(session: Session, peerId: string): Promise<boolean> { return true; } async isAllowedToSendToPeer(session: Session, peerId: string): Promise<boolean> { return true; } async authenticate(token: any): Promise<Session> { throw new Error('Authentication not implemented'); } transformError(err: Error) { return err; } }
これを使用するには、RpcKernel
にプロバイダを渡します:
const kernel = new RpcKernel([{provide: RpcKernelSecurity, useClass: MyKernelSecurity, scope: 'rpc'}]);
また、Deepkit アプリの場合は、アプリ内でプロバイダを使って RpcKernelSecurity
クラスをオーバーライドします:
import { App } from '@deepkit/type'; import { RpcKernelSecurity } from '@deepkit/rpc'; import { FrameworkModule } from '@deepkit/framework'; new App({ controllers: [MyRpcController], providers: [ {provide: RpcKernelSecurity, useClass: MyKernelSecurity, scope: 'rpc'} ], imports: [new FrameworkModule] }).run();
認証 / セッション
デフォルトでは、Session
オブジェクトは匿名セッションであり、クライアントが認証されていないことを意味します。クライアントが認証したい場合、authenticate
メソッドが呼び出されます。authenticate
メソッドが受け取るトークンはクライアントから送られ、任意の値を取り得ます。
クライアントがトークンを設定すると、最初の RPC 関数が呼び出されたとき、または client.connect()
が手動で実行されたときに認証が行われます。
const client = new RpcWebSocketClient('localhost:8081'); client.token.set('123456789'); const controller = client.controller<Controller>('/main');
この場合、RpcKernelSecurity.authenticate
はトークン 123456789
を受け取り、それに応じて別のセッションを返すことができます。返されたセッションは、その後 hasControllerAccess
のような他のすべてのメソッドに渡されます。
import { Session, RpcKernelSecurity } from '@deepkit/rpc'; class UserSession extends Session { } class MyKernelSecurity extends RpcKernelSecurity { async hasControllerAccess(session: Session, controllerAccess: RpcControllerAccess): Promise<boolean> { if (controllerAccess.controllerClassType instanceof MySecureController) { //MySecureController には UserSession が必要です return session instanceof UserSession; } return true; } async authenticate(token: any): Promise<Session> { if (token === '123456789') { //username は ID でもユーザー名でもかまいません return new UserSession('username', token); } throw new Error('Authentication failed'); } }
コントローラーアクセス
hasControllerAccess
メソッドは、クライアントが特定の RPC 関数を実行することを許可されているかどうかを判定します。このメソッドは、すべての RPC 関数呼び出しに対して呼び出されます。false
を返した場合、アクセスは拒否され、クライアント側でエラーが送出されます。
RpcControllerAccess
には RPC 関数に関する有用な情報が含まれます:
interface RpcControllerAccess { controllerName: string; controllerClassType: ClassType; actionName: string; actionGroups: string[]; actionData: { [name: string]: any }; }
グループと追加データはデコレーター @rpc.action()
を介して変更できます:
class Controller { @rpc.action().group('secret').data('role', 'admin') saveUser(user: User): void { } } class MyKernelSecurity extends RpcKernelSecurity { async hasControllerAccess(session: Session, controllerAccess: RpcControllerAccess): Promise<boolean> { if (controllerAccess.actionGroups.includes('secret')) { if (session instanceof UserSession) { //todo: 確認 return session.username === 'admin'; } return false; } return true; } }