Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

箱庭システムのWebサーバー要件 #56

Open
tmori opened this issue Aug 18, 2024 · 2 comments
Open

箱庭システムのWebサーバー要件 #56

tmori opened this issue Aug 18, 2024 · 2 comments

Comments

@tmori
Copy link
Contributor

tmori commented Aug 18, 2024

箱庭システムのWebサーバー要件

要件1: Webサーバーの作成

  • 目的: クライアントと通信するためのWebサーバーを構築する。
  • 機能:
    • サーバーが起動すると、クライアントが接続できるWebSocketサーバーを立ち上げる。

要件2: 公開PDU定義ファイルの参照と準備

  • 目的: サーバー起動時に、PDU定義ファイルを読み込み、通信に必要な設定やデータを準備する。
  • 機能:
    • PDU定義ファイルには、送受信するデータの構造や許可された操作が記述されている。
    • サーバーはこの定義に基づいて、必要なリソースやメモリを確保する。

要件3: クライアントからのWebSocket接続要求の処理

  • 目的: クライアントからの接続要求を受け付け、適切に対応する。
  • 機能:
    • クライアントが接続を要求した場合、サーバーは接続を確立し、PDU定義に基づいてデータの送受信を管理する。

要件4: 公開PDUデータの送信

  • 目的: クライアントに対して、共有メモリから読み込んだPDUデータを定義された周期で送信する。
  • 機能:
    • 共有メモリから必要なデータを読み込み、PDU定義に基づいた周期でクライアントに送信する。

要件5: クライアントからのPDUデータ送信と処理

  • 目的: クライアントが許可されたPDUデータをサーバーに送信し、サーバーがそれを共有メモリに書き込む。
  • 機能:
    • サーバーはクライアントからのPDUデータを受信し、それを箱庭システムの共有メモリに書き込む。

要件6: クライアントによる接続終了

  • 目的: クライアントが接続を終了したい場合に、正常に接続を終了させる。
  • 機能:
    • クライアントはWebSocketのクローズフレームを送信し、接続を終了する。
    • サーバーは接続がクローズされたことを検知し、対応するデータを適切に処理する。

要件7: クローズ後のデータパージ

  • 目的: クライアントが接続を終了した後、そのクライアントに関連するデータをサーバーから削除する。
  • 機能:
    • クライアントが接続を終了した際、そのクライアントに関するデータをパージ(削除)し、リソースを解放する。

要件8: クライアントからのデータリクエストへの応答

  • 目的: クライアントがPDUデータをリクエストした場合に、公開許可されたPDUデータをJSON形式で通知する。
  • 機能:
    • クライアントがデータをリクエストした際、サーバーは共有メモリから該当データを取得し、JSON形式でクライアントに送信する。

確認点と補足

  • リソース管理: 共有メモリの読み書きやクライアントごとのデータ管理において、パフォーマンスやリソースの効率的な利用が重要です。
  • エラーハンドリング: 接続の途中でエラーが発生した場合や、クライアントからの無効なリクエストに対してどのように対処するかを考慮します。
  • セキュリティ: クライアントの認証やデータの暗号化など、通信の安全性を確保するためのセキュリティ対策が必要かどうかを検討します。
@tmori
Copy link
Contributor Author

tmori commented Aug 18, 2024

セキュリティを考慮した設計

プレーンな実装から始めて、セキュリティを後から追加できるように設計するというアプローチは、非常に合理的で、柔軟性のある方法です。セキュリティチェックやエンコード/デコード機能を後から簡単に追加できるようにしておくことで、プロジェクトの初期段階では迅速に開発を進めながら、必要に応じてセキュリティ機能を強化することが可能になります。

セキュリティを考慮したインタフェース設計のメリット

  1. 拡張性の確保

    • 最初はプレーンなWebSocketのI/O処理でシンプルに始めることで、開発をスピードアップできます。しかし、インタフェースをセキュリティ対応可能な形で設計しておけば、後からセキュリティ機能(認証、暗号化、データ検証など)を容易に追加できます。
  2. モジュール化

    • セキュリティ機能をインタフェースとして分離することで、セキュリティ層とビジネスロジック層を分けて管理できるようになります。このモジュール化により、セキュリティの変更や強化を行う際に、他のシステム部分への影響を最小限に抑えることができます。
  3. コードの見通し

    • セキュリティ関連の処理が専用のインタフェースやモジュールにまとめられるため、コード全体の見通しが良くなります。セキュリティ機能の実装やテストも集中して行えるようになります。

インタフェース設計の例

以下のように、セキュリティチェックやエンコード/デコードのインタフェースを用意しておくと、後で必要な機能を追加することが容易になります。

1. セキュリティインタフェースの定義

class SecurityInterface:
    def encode(self, data: dict) -> str:
        """データをエンコードして送信可能な形式に変換"""
        return json.dumps(data)

    def decode(self, data: str) -> dict:
        """受信データをデコードして処理可能な形式に変換"""
        return json.loads(data)

    def validate(self, data: dict) -> bool:
        """データが正当かどうかを検証"""
        # プレーンな実装では常にTrueを返す
        return True

    def authorize(self, client_id: str) -> bool:
        """クライアントが認可されているかどうかを確認"""
        # プレーンな実装では常にTrueを返す
        return True

2. WebSocket I/O処理にセキュリティインタフェースを統合

import asyncio
import websockets
import json

# セキュリティインタフェースを統合
class SecureWebSocketHandler:
    def __init__(self, security_interface: SecurityInterface):
        self.security = security_interface

    async def handle_client(self, websocket, path):
        client_id = str(websocket.remote_address)
        
        if not self.security.authorize(client_id):
            await websocket.close()
            return

        try:
            async for message in websocket:
                decoded_message = self.security.decode(message)

                if not self.security.validate(decoded_message):
                    await websocket.send("Invalid data")
                    continue

                # ビジネスロジック処理
                response = self.handle_business_logic(decoded_message)

                # エンコードして送信
                encoded_response = self.security.encode(response)
                await websocket.send(encoded_response)

        except websockets.exceptions.ConnectionClosed:
            pass

    def handle_business_logic(self, data):
        # ここにビジネスロジックを実装
        return {"status": "ok"}

# プレーンなセキュリティインタフェースを使用してサーバーを起動
security = SecurityInterface()
handler = SecureWebSocketHandler(security)

start_server = websockets.serve(handler.handle_client, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

今後のセキュリティ強化

  • エンコード/デコードの実装: 初期段階ではJSONのシリアライズ/デシリアライズだけですが、必要に応じて暗号化や特定フォーマットへの変換を追加可能です。
  • バリデーションと認可の強化: セキュリティのニーズに応じて、トークン認証やデータの整合性チェックを追加していけます。

結論

セキュリティチェック用のインタフェースを最初から設計に組み込むことで、柔軟性と拡張性のあるシステムを構築できます。プレーンな実装でスタートし、後からセキュリティ機能を段階的に追加することで、開発効率を高めつつ、セキュアなシステムを実現することができます。このアプローチは、プロジェクトの進行に応じたセキュリティ強化を容易にします。

@tmori
Copy link
Contributor Author

tmori commented Aug 18, 2024

実装の動的切り替え方法

はい、そのアプローチが良さそうです。最初はプレーンな実装で進めて、必要に応じてセキュリティライブラリを動的にインポートして組み込む形にすることで、柔軟かつ効率的にセキュリティを強化できます。

動的にライブラリをインポートして切り替える方法

  1. ライブラリの動的インポート

    • Pythonでは、importlibを使ってライブラリを動的にインポートすることができます。これにより、セキュリティインタフェースの実装を状況に応じて切り替えることが可能になります。
  2. ファクトリーパターンの活用

    • ファクトリーパターンを使って、セキュリティインタフェースのインスタンスを作成する際に、使用するセキュリティライブラリを選択します。これにより、コード全体の構造を変更せずに、セキュリティの実装を切り替えられます。

サンプル実装

import importlib

class SecurityFactory:
    @staticmethod
    def create_security_interface(lib_name=None):
        if lib_name is None:
            # プレーンなセキュリティインタフェースを使用
            return SecurityInterface()
        else:
            # 指定されたライブラリを動的にインポートしてインタフェースを実装
            module = importlib.import_module(lib_name)
            class_name = lib_name.capitalize() + "SecurityInterface"
            return getattr(module, class_name)()

# セキュリティインタフェースのプレーン実装
class SecurityInterface:
    def encode(self, data: dict) -> str:
        return json.dumps(data)

    def decode(self, data: str) -> dict:
        return json.loads(data)

    def validate(self, data: dict) -> bool:
        return True

    def authorize(self, client_id: str) -> bool:
        return True

    def authenticate(self, token: str) -> bool:
        return True

# 使用例
# プレーンなセキュリティインタフェースを使用
security = SecurityFactory.create_security_interface()

# 特定のライブラリ(例: 'fastapi_jwt')を使用
# security = SecurityFactory.create_security_interface('fastapi_jwt')

handler = SecureWebSocketHandler(security)
start_server = websockets.serve(handler.handle_client, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

動的切り替えの手順

  1. セキュリティライブラリの準備

    • 各セキュリティライブラリ用に、SecurityInterfaceを実装したクラスを用意します。例えば、fastapi_jwt.pyモジュールでは、FastapiJwtSecurityInterfaceクラスがSecurityInterfaceを継承して実装されます。
  2. ファクトリメソッドでの選択

    • プロジェクトの設定や環境変数に基づいて、ファクトリメソッドで使用するセキュリティライブラリを選択します。
  3. セキュリティインタフェースのインスタンス化

    • 選択されたライブラリに基づいて、適切なSecurityInterfaceの実装をインスタンス化し、それを使用してWebSocketサーバーを起動します。

まとめ

このように動的にライブラリをインポートし、セキュリティインタフェースの実装を切り替えることで、柔軟なセキュリティ管理が可能になります。最初はプレーンな実装から始めて、必要に応じてライブラリを切り替えることで、開発のスピードとセキュリティの強化を両立できます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant