# 画面設計書 193-SSHキー一覧

## 概要

本ドキュメントは、ユーザーのSSHキー一覧画面の設計書である。SSHキーの登録・管理を行う画面の仕様を定義する。

### 本画面の処理概要

ユーザーが自身のアカウントに登録しているSSHキーを一覧表示し、新規SSHキーの追加および既存キーの削除を行う画面である。SSHキーはGitLabとの安全な通信を確立するために使用される。

**業務上の目的・背景**：SSHキーはGitリポジトリへのセキュアなアクセスを提供する重要な認証手段である。開発者は複数のマシンや用途（認証、署名）に応じて複数のSSHキーを管理する必要がある。本画面は、キーのライフサイクル管理（登録、有効期限設定、失効、削除）を一元的に行うために不可欠である。

**画面へのアクセス方法**：
1. 右上のユーザーアバターをクリック
2. 「設定」を選択
3. 左サイドバーから「SSHキー」を選択
4. または URL `/-/user_settings/ssh_keys` に直接アクセス

**主要な操作・処理内容**：
1. 登録済みSSHキーの一覧表示
2. 新規SSHキーの追加（公開鍵、タイトル、用途タイプ、有効期限の設定）
3. SSHキーの削除
4. SSHキーの失効（Revoke）

**画面遷移**：
- 遷移元: ユーザー設定メニュー
- 遷移先: SSHキー詳細画面（キー選択時）

**権限による表示制御**：
- ログイン必須
- 自身のSSHキーのみ表示・管理可能

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 80 | SSHキー管理 | 主機能 | SSHキーの管理 |

## 画面種別

一覧 / 登録

## URL/ルーティング

```
GET    /-/user_settings/ssh_keys         # 一覧表示
POST   /-/user_settings/ssh_keys         # キー登録
DELETE /-/user_settings/ssh_keys/:id     # キー削除
DELETE /-/user_settings/ssh_keys/:id/revoke  # キー失効
```

## 入出力項目

### 新規SSHキー登録フォーム

| 項目名 | 項目ID | 入出力 | 型 | 必須 | 説明 |
|--------|--------|--------|-----|------|------|
| 公開鍵 | key | 入力 | text | Yes | SSH公開鍵（ssh-rsa, ssh-ed25519等で始まる） |
| タイトル | title | 入力 | string | Yes | キーの識別名（例：MacBook key） |
| 用途タイプ | usage_type | 入力 | integer | No | 0:認証&署名, 1:認証のみ, 2:署名のみ |
| 有効期限 | expires_at | 入力 | date | No | YYYY-MM-DD形式、明日以降 |

## 表示項目

### キー一覧テーブル

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| タイトル | @keys[].title | キーのタイトル |
| キー（フィンガープリント） | @keys[].fingerprint_sha256 | SHA256フィンガープリント |
| 用途タイプ | @keys[].usage_type | 認証/署名/両方 |
| 作成日 | @keys[].created_at | 登録日時 |
| 最終使用日 | @keys[].last_used_at | 最後に使用された日時 |
| 有効期限 | @keys[].expires_at | 有効期限日 |

### 統計情報

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| キー数 | @keys.count | 登録済みキーの総数 |

## イベント仕様

### 1-SSHキー追加

**トリガー**: 「Add key」ボタン押下

**処理フロー**:
1. フォームデータのバリデーション（クライアント側で公開鍵形式チェック）
2. 秘密鍵警告の表示（秘密鍵が貼り付けられた場合）
3. POSTリクエストで `Keys::CreateService` を呼び出し
4. 成功時: SSHキー詳細画面へリダイレクト
5. 失敗時: エラーメッセージを表示して画面に留まる

### 2-SSHキー削除

**トリガー**: 削除ボタン押下

**処理フロー**:
1. 削除確認ダイアログを表示
2. 確認後、DELETE リクエスト
3. `Keys::DestroyService` でキーを削除
4. authorized_keys ファイルからキーを削除
5. 一覧画面へリダイレクト

### 3-SSHキー失効

**トリガー**: 「Revoke」ボタン押下

**処理フロー**:
1. 失効確認ダイアログを表示
2. 確認後、DELETE `/revoke` リクエスト
3. `Keys::RevokeService` でキーを失効処理
4. 一覧画面へリダイレクト

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| SSHキー追加 | keys | INSERT | 新規SSHキーの登録 |
| SSHキー削除 | keys | DELETE | SSHキーの削除 |
| SSHキー失効 | keys | DELETE | SSHキーの失効（削除） |

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

#### keys

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | current_user.id | キー所有者 |
| INSERT | organization_id | Current.organization.id | 組織ID |
| INSERT | title | フォーム入力値 | キータイトル |
| INSERT | key | フォーム入力値 | 公開鍵文字列 |
| INSERT | usage_type | フォーム入力値 | 用途タイプ |
| INSERT | expires_at | フォーム入力値 | 有効期限 |
| INSERT | fingerprint | 自動生成 | MD5フィンガープリント |
| INSERT | fingerprint_sha256 | 自動生成 | SHA256フィンガープリント |
| INSERT | created_at | 現在日時 | 作成日時 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG-001 | 情報 | SSH keys allow you to establish a secure connection between your computer and GitLab. | ページ説明 |
| MSG-002 | 警告 | Are you sure? Publicly visible private SSH keys can compromise your system. | 秘密鍵検出時 |
| MSG-003 | エラー | Key has expired | 期限切れキー追加時 |
| MSG-004 | エラー | cannot be used because it belongs to a compromised private key | 禁止キー使用時 |
| MSG-005 | 情報 | Key titles are publicly visible. | タイトル説明 |

## 例外処理

| 例外 | 発生条件 | 対応処理 |
|-----|---------|---------|
| 未認証 | ログインしていない | ログイン画面へリダイレクト |
| バリデーションエラー | 公開鍵形式が不正 | エラーメッセージを表示 |
| 重複エラー | 同じフィンガープリントのキーが存在 | エラーメッセージを表示 |
| 禁止キー | 漏洩した秘密鍵に対応する公開鍵 | エラーメッセージを表示 |
| 有効期限ポリシー | インスタンス設定の有効期限制限を超過 | エラーメッセージを表示 |

## 備考

- SSH公開鍵は `ssh-rsa`, `ssh-ed25519`, `ecdsa-sha2-nistp256` 等のアルゴリズムに対応
- インスタンス設定で最大有効期限が制限される場合がある
- FIPS モードではMD5フィンガープリントは生成されない
- 禁止（compromised）キーリストと照合される

---

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

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

### 推奨読解順序

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

SSHキー（Key）のデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | key.rb | `app/models/key.rb` | Keyモデルの属性・バリデーション・コールバックを確認 |

**読解のコツ**: `Key` モデルは `Expirable` モジュールをインクルードしており、有効期限管理が組み込まれている。`usage_type` はenumで定義されている。

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

処理の起点となるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ssh_keys_controller.rb | `app/controllers/user_settings/ssh_keys_controller.rb` | index/show/create/destroy/revokeアクションの実装を確認 |

**主要処理フロー**:
1. **8-11行目**: `index` アクション - キー一覧取得と新規キーオブジェクト初期化
2. **18-27行目**: `create` アクション - Keys::CreateService によるキー登録
3. **30-38行目**: `destroy` アクション - Keys::DestroyService によるキー削除
4. **40-48行目**: `revoke` アクション - Keys::RevokeService によるキー失効

#### Step 3: ビューレイヤーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.html.haml | `app/views/user_settings/ssh_keys/index.html.haml` | 全体構造を確認 |
| 3-2 | _form.html.haml | `app/views/user_settings/ssh_keys/_form.html.haml` | 登録フォームを確認 |
| 3-3 | _key_table.html.haml | `app/views/user_settings/ssh_keys/_key_table.html.haml` | キー一覧テーブルを確認 |

**主要処理フロー**:
- **6行目**: SettingsSectionComponent でセクション構成
- **13-28行目**: CrudComponent で一覧・フォーム表示

#### Step 4: サービスレイヤーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | create_service.rb | `app/services/keys/create_service.rb` | キー登録ロジック |
| 4-2 | destroy_service.rb | `app/services/keys/destroy_service.rb` | キー削除ロジック |
| 4-3 | revoke_service.rb | `app/services/keys/revoke_service.rb` | キー失効ロジック |

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

```
UserSettings::SshKeysController#index
    │
    ├─ current_user.keys.order_id_desc
    │      └─ @keys
    │
    └─ Key.new
           └─ @key (空のキーオブジェクト)

UserSettings::SshKeysController#create
    │
    └─ Keys::CreateService#execute
           ├─ Key.new(params)
           ├─ key.save
           ├─ add_to_authorized_keys (callback)
           │      └─ AuthorizedKeysWorker.perform_async
           └─ SystemHooksService#execute_hooks_for

UserSettings::SshKeysController#destroy
    │
    └─ Keys::DestroyService#execute
           ├─ key.destroy
           ├─ remove_from_authorized_keys (callback)
           │      └─ AuthorizedKeysWorker.perform_async
           └─ SystemHooksService#execute_hooks_for
```

### データフロー図

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

公開鍵文字列 ───▶ Gitlab::SSHPublicKey.sanitize ───▶ sanitized key
    │                      │
    └──────────────────────┼──────────────────────▶ keys テーブル
                           │
                           ├─ generate_fingerprint
                           │      ├─ fingerprint_md5
                           │      └─ fingerprint_sha256
                           │
                           └─ AuthorizedKeysWorker
                                  └─ authorized_keys ファイル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ssh_keys_controller.rb | `app/controllers/user_settings/ssh_keys_controller.rb` | コントローラー | リクエスト処理 |
| index.html.haml | `app/views/user_settings/ssh_keys/index.html.haml` | テンプレート | 一覧画面 |
| _form.html.haml | `app/views/user_settings/ssh_keys/_form.html.haml` | パーシャル | 登録フォーム |
| _key_table.html.haml | `app/views/user_settings/ssh_keys/_key_table.html.haml` | パーシャル | キー一覧テーブル |
| _key.html.haml | `app/views/user_settings/ssh_keys/_key.html.haml` | パーシャル | キー行表示 |
| key.rb | `app/models/key.rb` | モデル | SSHキーデータ定義 |
| create_service.rb | `app/services/keys/create_service.rb` | サービス | キー登録ロジック |
| destroy_service.rb | `app/services/keys/destroy_service.rb` | サービス | キー削除ロジック |
| revoke_service.rb | `app/services/keys/revoke_service.rb` | サービス | キー失効ロジック |
| user_settings.rb | `config/routes/user_settings.rb` | 設定 | ルーティング定義 |
| ssh_public_key.rb | `lib/gitlab/ssh_public_key.rb` | ライブラリ | SSH公開鍵パース |
