# 画面設計書 79-セキュリティ設定画面

## 概要

本ドキュメントはChatwootのセキュリティ設定画面に関する設計書です。SAMLシングルサインオン（SSO）の設定を管理する画面について記述しています。

### 本画面の処理概要

本画面では、SAML（Security Assertion Markup Language）を使用したシングルサインオン（SSO）の設定を行います。企業の既存のIdP（Identity Provider）とChatwootを連携させることで、従業員は既存の認証情報でChatwootにログインできるようになります。

**業務上の目的・背景**：企業のセキュリティポリシーでは、すべてのアプリケーションを一元的な認証基盤で管理することが求められることがあります。SAMLを使用することで、Active Directory、Okta、OneLoginなどのIdPと連携し、パスワード管理の一元化、セキュリティの向上、コンプライアンス要件への対応が可能になります。

**画面へのアクセス方法**：サイドバーの「設定」から「セキュリティ」メニューを選択してアクセスします。または、URLで直接 `/app/accounts/:accountId/settings/security` にアクセスすることもできます。

**主要な操作・処理内容**：
1. SAML SSOの有効化/無効化：トグルスイッチで切り替え
2. SAML設定の入力：SSO URL、IdPエンティティID、証明書の入力
3. 設定の保存：入力内容をバックエンドに保存
4. SP情報の表示：Chatwoot側のSPエンティティIDとフィンガープリントを表示

**画面遷移**：
- 遷移元：設定ホーム画面
- 遷移先：なし

**権限による表示制御**：管理者（administrator）権限を持つユーザーのみがアクセス可能です。Cloud/Enterprise版でのみ利用可能で、SAMLが許可されたログイン方法として設定されている場合のみ設定フォームが表示されます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 197 | SAML設定 | 主機能 | SAML等のセキュリティ設定 |
| 9 | SAML設定 | 補助機能 | エンタープライズSAML設定 |

## 画面種別

設定

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| パス | `/app/accounts/:accountId/settings/security` |
| ルート名 | `security_settings_index` |
| コンポーネント | `Index.vue` |

## 入出力項目

### SAML設定フォーム（SamlSettings）

| 項目名 | 項目ID | 種別 | 型 | 必須 | バリデーション | 説明 |
|--------|--------|------|-----|------|---------------|------|
| SSO URL | ssoUrl | 入力 | String | Yes | required, url形式 | IdPのSSOエンドポイントURL |
| IdPエンティティID | idpEntityId | 入力 | String | Yes | required | IdPのエンティティID |
| 証明書 | certificate | 入力 | String | Yes | required | IdPの公開証明書（PEM形式） |
| 有効/無効 | isEnabled | 入力 | Boolean | No | - | SAML SSOの有効/無効フラグ |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| SP エンティティID | spEntityId | Chatwoot側のSPエンティティID |
| フィンガープリント | fingerprint | 証明書のフィンガープリント |
| SAML有効状態 | isEnabled | SAML SSOの有効/無効状態 |
| 設定フォーム | formState | 設定入力フォーム（条件付き表示） |

## イベント仕様

### 1-SAML有効化トグル

**トリガー**: Switchコンポーネントのトグル

**処理フロー**:
1. `isEnabled`の値が変更される
2. `toggleSaml`（@change）が呼び出される
3. 無効化時は`handleDisable`を実行
4. `saveSamlSettings`で空の設定を送信（削除処理）

### 2-設定保存ボタン押下

**トリガー**: 「Update」ボタンのクリック

**処理フロー**:
1. `handleSubmit`を実行
2. Vuelidateでバリデーション実行（`v$.$touch()`）
3. バリデーションエラーがあれば処理中断
4. `saveSamlSettings`で設定を送信
5. 新規の場合は`samlSettingsAPI.create`、既存の場合は`samlSettingsAPI.update`
6. 成功時、レスポンスからfingerprint、spEntityIdを更新
7. 成功アラートを表示

### 3-画面マウント

**トリガー**: 画面のマウント

**処理フロー**:
1. `onMounted`で`loadSamlSettings`を実行
2. `samlSettingsAPI.get`で既存設定を取得
3. 設定が存在する場合、フォームにプリフィル
4. 404エラーの場合は初期状態を維持（設定なし）

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| SAML設定作成 | saml_settings | INSERT | 新しいSAML設定の作成 |
| SAML設定更新 | saml_settings | UPDATE | SAML設定の更新 |
| SAML設定削除 | saml_settings | DELETE | SAML設定の削除（無効化時） |

### テーブル別更新項目詳細

#### saml_settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | account_id | 現在のアカウントID | - |
| INSERT | sso_url | フォーム入力値 | IdPのSSO URL |
| INSERT | certificate | フォーム入力値 | 公開証明書 |
| INSERT | idp_entity_id | フォーム入力値 | IdPエンティティID |
| INSERT | sp_entity_id | 自動生成 | SP側のエンティティID |
| INSERT | fingerprint | 自動生成 | 証明書フィンガープリント |
| UPDATE | sso_url | フォーム入力値 | 編集時 |
| UPDATE | certificate | フォーム入力値 | 編集時 |
| UPDATE | idp_entity_id | フォーム入力値 | 編集時 |
| DELETE | id | 既存設定のID | 無効化時 |

## メッセージ仕様

| 種別 | キー | メッセージ | 表示条件 |
|------|-----|-----------|---------|
| 成功 | SECURITY_SETTINGS.SAML.API.SUCCESS | SAML設定が正常に保存されました | 設定保存成功時 |
| 成功 | SECURITY_SETTINGS.SAML.API.DISABLED | SAML SSOが無効化されました | SAML無効化成功時 |
| エラー | SECURITY_SETTINGS.SAML.API.ERROR | SAML設定の保存に失敗しました | 設定保存失敗時 |
| エラー | SECURITY_SETTINGS.SAML.API.ERROR_LOADING | SAML設定の読み込みに失敗しました | 設定取得失敗時 |
| バリデーション | SECURITY_SETTINGS.SAML.VALIDATION.SSO_URL_ERROR | SSO URLを入力してください | SSO URL未入力時 |
| バリデーション | SECURITY_SETTINGS.SAML.VALIDATION.CERTIFICATE_ERROR | 証明書を入力してください | 証明書未入力時 |
| バリデーション | SECURITY_SETTINGS.SAML.VALIDATION.IDP_ENTITY_ID_ERROR | IdPエンティティIDを入力してください | IdPエンティティID未入力時 |
| 無効状態 | SECURITY_SETTINGS.SAML_DISABLED_MESSAGE | SAML SSOは現在利用できません | SAMLが許可されていない場合 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 権限不足 | 管理者権限がない場合、画面にアクセスできない |
| 機能制限 | Cloud/Enterprise版以外では画面にアクセスできない |
| SAML非許可 | SAMLがallowedLoginMethodsに含まれない場合、無効メッセージを表示 |
| Paywall | SAML機能が無効なプランの場合、SamlPaywallを表示 |
| 設定取得エラー（404） | 初期状態として処理（エラー表示なし） |
| 設定取得エラー（その他） | エラーアラートを表示 |
| バリデーションエラー | 各フィールドにエラーメッセージを表示 |

## 備考

- Cloud/Enterprise版でのみ利用可能
- allowedLoginMethodsに'saml'が含まれている必要がある
- 証明書はPEM形式で入力
- SPエンティティIDとフィンガープリントはIdP側の設定に使用
- SAML設定は「Beta」機能としてマーク
- SamlInfoSectionでSP情報を表示
- SamlAttributeMapで属性マッピング情報を表示

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、SAML設定のデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | samlSettings.js (API) | `app/javascript/dashboard/api/samlSettings.js` | APIクライアントの構造、CRUD操作 |

**読解のコツ**: SAML設定はアカウントスコープのAPIで、get/create/update/deleteの4操作を提供。データはsaml_settingsオブジェクトとして送受信。

#### Step 2: エントリーポイントを理解する

処理の起点となるVueコンポーネントを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Index.vue | `app/javascript/dashboard/routes/dashboard/settings/security/Index.vue` | 条件分岐ロジック、Paywall表示判定 |

**主要処理フロー**:
1. **行13-15**: `allowedLoginMethods`でSAML許可状態を確認
2. **行17-19**: `isSamlSsoEnabled`でSAMLが有効かチェック
3. **行21-28**: `shouldShowSaml`で権限とSAML有効状態を組み合わせて判定
4. **行30**: `showPaywall`でPaywall表示判定

#### Step 3: SAML設定コンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SamlSettings.vue | `app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue` | フォーム実装、バリデーション、API呼び出し |

**主要処理フロー**:
- **行35-39**: Vuelidateバリデーション定義
- **行63-88**: `loadSamlSettings`で既存設定を取得
- **行90-131**: `saveSamlSettings`で設定を保存
- **行133-145**: `handleSubmit`でフォーム送信処理
- **行147-157**: `handleDisable`でSAML無効化処理
- **行159-163**: `toggleSaml`でトグル変更時の処理

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

```
Index.vue (エントリーポイント)
    │
    ├─ computed: shouldShowSaml
    │      ├─ shouldShow(FEATURE_FLAGS.SAML, ['administrator'], [...])
    │      └─ isSamlSsoEnabled (allowedLoginMethods.includes('saml'))
    │
    ├─ computed: showPaywall
    │      └─ shouldShowPaywall('saml')
    │
    ├─ SamlPaywall (条件付き表示)
    │      └─ Paywall状態の場合に表示
    │
    └─ SamlSettings (条件付き表示)
           │
           ├─ onMounted
           │      └─ loadSamlSettings
           │             └─ samlSettingsAPI.get()
           │
           ├─ Switch (有効/無効トグル)
           │      └─ @change → toggleSaml
           │             └─ handleDisable (無効化時)
           │                    └─ saveSamlSettings({})
           │                           └─ samlSettingsAPI.delete()
           │
           ├─ SamlInfoSection (SP情報表示)
           │      └─ fingerprint, spEntityId表示
           │
           ├─ SamlAttributeMap (属性マッピング表示)
           │
           └─ form
                  └─ @submit → handleSubmit
                         └─ saveSamlSettings(settings)
                                ├─ samlSettingsAPI.create() (新規)
                                └─ samlSettingsAPI.update() (更新)
```

### データフロー図

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

SAMLフォーム ────────────▶ handleSubmit ────────────────▶ API送信
      │                        │
      ├─ ssoUrl               │
      ├─ idpEntityId          ▼
      └─ certificate     samlSettingsAPI.create/update ─▶ 設定保存
                               │
                               ▼
                        レスポンス ────────────────────▶ fingerprint
                               │                         spEntityId更新
                               │
                               ▼
                        useAlert ─────────────────────▶ 成功メッセージ

[設定読み込み]

onMounted ─────────────▶ loadSamlSettings ─────────────▶ API取得
       │                        │
       │                        ▼
       │                 samlSettingsAPI.get() ────────▶ 設定取得
       │                        │
       │                        ▼
       └────────────────── formState更新 ──────────────▶ フォームプリフィル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Index.vue | `app/javascript/dashboard/routes/dashboard/settings/security/Index.vue` | ソース | メイン画面コンポーネント |
| SamlSettings.vue | `app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue` | ソース | SAML設定フォーム |
| SamlPaywall.vue | `app/javascript/dashboard/routes/dashboard/settings/security/components/SamlPaywall.vue` | ソース | Paywall表示 |
| SamlInfoSection.vue | `app/javascript/dashboard/routes/dashboard/settings/security/components/SamlInfoSection.vue` | ソース | SP情報表示 |
| SamlAttributeMap.vue | `app/javascript/dashboard/routes/dashboard/settings/security/components/SamlAttributeMap.vue` | ソース | 属性マッピング表示 |
| samlSettings.js (API) | `app/javascript/dashboard/api/samlSettings.js` | ソース | API通信クラス |
| security.routes.js | `app/javascript/dashboard/routes/dashboard/settings/security/security.routes.js` | ソース | ルーティング定義 |
| usePolicy.js | `app/javascript/dashboard/composables/usePolicy.js` | ソース | 権限判定composable |
