# 通知設計書 6-証明書エラー

## 概要

本ドキュメントは、InvenTreeモバイルアプリケーションにおける「証明書エラー」通知の設計仕様を定義する。HTTPS通信時にサーバー証明書が無効と判断された場合に表示されるエラー通知の仕様を記載する。

### 本通知の処理概要

この通知は、InvenTreeサーバーへのHTTPS接続時にSSL/TLS証明書の検証に失敗した場合、つまり`CertificateException`または`HandshakeException`が発生した際にユーザーに通知する機能である。

**業務上の目的・背景**：セキュアな通信はデータ保護の基本である。証明書エラーは、自己署名証明書の使用、証明書の期限切れ、ドメイン不一致、または中間者攻撃（MITM）の可能性を示す。ユーザーにこの問題を明確に通知することで、セキュリティリスクを認識させ、適切な対処（証明書の更新、設定変更、管理者への連絡）を促すことができる。

**通知の送信タイミング**：HTTP通信を行う際に以下の例外がキャッチされた場合にトリガーされる：
- `CertificateException`: 証明書が無効
- `HandshakeException`: TLSハンドシェイクの失敗
- `strictHttps`設定が有効で、`badCertificateCallback`で証明書エラーが検出された場合

**通知の受信者**：アプリケーションを操作している現在のユーザー。画面上にスナックバーとして表示され、タップすると詳細ダイアログが開く。

**通知内容の概要**：「Server Certificate Error」（サーバー証明書エラー）というタイトルと、証明書検証の失敗詳細が表示される。詳細ボタンをタップすると、URLと例外メッセージを含む詳細情報が表示される。

**期待されるアクション**：ユーザーは通知を確認し、以下のような対処を行う：
- サーバー管理者に証明書の更新を依頼
- 自己署名証明書の場合、アプリ設定で「厳格なHTTPS」を無効化
- ドメイン名とサーバーURLが一致しているか確認
- 公共Wi-Fiなど信頼できないネットワークの使用を避ける

## 通知種別

アプリ内通知（スナックバー + 詳細ダイアログ + 音声）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高 |
| リトライ | 無し（UI通知のため） |

### 送信先決定ロジック

現在アプリケーションを操作しているユーザーに対して、画面下部にスナックバー形式で表示する。`showServerError`関数経由で表示され、詳細ダイアログへのアクセスも提供される。

## 通知テンプレート

### スナックバー通知の場合

| 項目 | 内容 |
|-----|------|
| 表示位置 | 画面下部 |
| 背景色 | Colors.deepOrange（エラー時） |
| 表示時間 | 10秒（アクションボタンあり） |
| 形式 | テキスト + 詳細ボタン |

### 本文テンプレート

```
[サーバー証明書エラー]  [詳細ボタン]
```

### 詳細ダイアログ

| 項目 | 内容 |
|-----|------|
| ダイアログタイプ | SimpleDialog |
| アイコン | TablerIcons.server |
| タイトル | L10().serverCertificateError |
| 本文 | error.toString() / L10().serverCertificateInvalid + URL |

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| serverCertificateError | エラータイトル | L10()（多言語リソース） | Yes |
| serverCertificateInvalid | 証明書無効メッセージ | L10()（多言語リソース） | No |
| error.toString() | エラー詳細 | CertificateException/HandshakeException | Yes |
| url | リクエストURL | API通信処理から | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API通信 | TLSハンドシェイク失敗 | HandshakeException発生時 | TLS接続の確立に失敗 |
| API通信 | 証明書検証失敗 | CertificateException発生時 | 証明書が無効 |
| API通信 | 厳格なHTTPS | strictHttps=true かつ badCertificate | 厳格モードで証明書エラー |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| コンテキストなし | `hasContext()`が`false`を返す場合 |
| 厳格モード無効 | `INV_STRICT_HTTPS`が`false`で、badCertificateCallbackがtrueを返す場合は接続を許可 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[HTTPS接続試行] --> B[TLSハンドシェイク]
    B --> C{証明書検証}
    C -->|有効| D[処理続行]
    C -->|無効| E{strictHttps?}
    E -->|true| F[showServerError呼び出し]
    E -->|false| G[接続を許可して続行]
    F --> H[音声再生]
    H --> I[スナックバー表示]
    I --> J{ユーザーが詳細タップ?}
    J -->|Yes| K[showErrorDialog表示]
    J -->|No| L[10秒後自動消去]
    K --> M[return null]
    L --> M
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| settings | 音声設定確認、厳格HTTPS設定 | Sembast NoSQL DB |

### テーブル別参照項目詳細

#### settings

| 参照項目（キー名） | 用途 | 取得条件 |
|-------------------|------|---------|
| INV_SOUNDS_SERVER | サーバーエラー音有効/無効 | デフォルト: true |
| INV_STRICT_HTTPS | 厳格なHTTPS検証 | デフォルト: false |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | 通知表示のみで更新なし |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| CertificateException | 証明書が無効 | showServerErrorで通知表示 |
| HandshakeException | TLSハンドシェイク失敗 | showServerErrorで通知表示 |
| badCertificateCallback | strictHttps=trueで証明書エラー | showServerErrorで通知表示、接続拒否 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回 |
| リトライ間隔 | - |
| リトライ対象エラー | - |

証明書エラーは設定変更なしでは解決しないため、自動リトライは行わない。

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（エラー発生時に即座に表示）

## セキュリティ考慮事項

- エラー詳細には証明書情報やURLが含まれる可能性がある
- 証明書エラーは中間者攻撃の可能性を示すため、ユーザーに警告する意義がある
- `strictHttps`設定を無効にすると、自己署名証明書を許容するが、セキュリティリスクが増加する
- デフォルトでは`strictHttps`は無効（自己署名証明書を許容）

## 備考

- `badCertificateCallback`はHttpClientの設定で証明書検証をカスタマイズ
- 自己署名証明書を使用する開発環境では`strictHttps`を無効にする必要がある
- 本番環境では有効な証明書を使用し、`strictHttps`を有効にすることを推奨
- `CertificateException`と`HandshakeException`は別々にキャッチされるが、同じ通知処理を使用

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: 設定値を理解する

まず、証明書検証に関連する設定値を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | preferences.dart | `lib/preferences.dart` | INV_STRICT_HTTPS定数の定義 |

**読解のコツ**: `INV_STRICT_HTTPS`は厳格なHTTPS検証を制御する設定キー。デフォルトは`false`。

#### Step 2: 証明書検証の仕組みを理解する

HttpClientの証明書コールバック設定を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | api.dart | `lib/api.dart` | createClientメソッドのbadCertificateCallback設定 |

**主要処理フロー**:
1. **行1177-1199**: `createClient()`メソッド - HttpClientの生成と証明書コールバック設定
2. **行1180-1193**: `badCertificateCallback` - 証明書エラー時の処理

#### Step 3: 例外キャッチ箇所を理解する

CertificateExceptionとHandshakeExceptionがキャッチされる箇所を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | api.dart | `lib/api.dart` | apiRequest、completeRequest、downloadFileでの例外キャッチ |

**主要処理フロー**:
1. **行1279-1284**: `apiRequest()`でのCertificateExceptionキャッチ
2. **行1284-1288**: `apiRequest()`でのHandshakeExceptionキャッチ
3. **行1381-1385**: `completeRequest()`でのCertificateExceptionキャッチ
4. **行940-944**: `downloadFile()`でのHandshakeExceptionキャッチ

### プログラム呼び出し階層図

```
InvenTreeAPI.createClient() [api.dart:1177]
    |
    +-- HttpClient() [api.dart:1178]
    |
    +-- badCertificateCallback [api.dart:1180]
            |
            +-- strictHttps チェック
            |       |
            |       +-- [true] --> showServerError() --> return false
            |       +-- [false] --> return true (接続許可)

InvenTreeAPI.apiRequest() [api.dart:1208]
    |
    +-- client.openUrl() [api.dart:1252]
    |       |
    |       +-- [CertificateException/HandshakeException発生]
    |
    +-- catch (CertificateException) [api.dart:1279]
    |       |
    |       +-- showServerError()
    |
    +-- catch (HandshakeException) [api.dart:1284]
            |
            +-- showServerError()
```

### データフロー図

```
[入力]                    [処理]                         [出力]

HTTPS接続            createClient()                スナックバー表示
     |          --->  badCertificateCallback  --> "サーバー証明書エラー"
     v                     |                        [詳細ボタン]
[証明書検証失敗]           v
                    strictHttps?
                    /         \
                [true]      [false]
                   |            |
                   v            v
            showServerError()  接続許可
                   |
                   v
            return false
            (接続拒否)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| api.dart | `lib/api.dart` | ソース | HTTP通信と証明書検証 |
| dialogs.dart | `lib/widget/dialogs.dart` | ソース | サーバーエラー通知表示 |
| snacks.dart | `lib/widget/snacks.dart` | ソース | スナックバー通知の表示 |
| helpers.dart | `lib/helpers.dart` | ソース | 音声再生・共通ヘルパー関数 |
| preferences.dart | `lib/preferences.dart` | ソース | INV_STRICT_HTTPS設定 |
| l10.dart | `lib/l10.dart` | ソース | 多言語対応（serverCertificateError等） |
