# 画面設計書 263-偽装トークン一覧

## 概要

本ドキュメントは、GitLab管理者エリアにおける「偽装トークン一覧」画面の設計を記載したものです。管理者が特定ユーザーの偽装トークン（Impersonation Token）を管理するための画面です。

### 本画面の処理概要

**業務上の目的・背景**：偽装トークンは、管理者がユーザーに代わってAPIを呼び出す際に使用される特別なアクセストークンです。これは、トラブルシューティングやサポート業務において、ユーザーのパスワードを知らなくてもユーザーの代わりに操作を行う必要がある場合に有用です。本画面では、特定ユーザーの偽装トークンの作成、一覧表示、取り消し、ローテーションを一元管理できます。

**画面へのアクセス方法**：管理者エリア > ユーザー一覧 > 対象ユーザーの詳細 > 偽装トークンタブ、または `/admin/users/:user_id/impersonation_tokens` に直接アクセスします。管理者権限と偽装機能の有効化が必要です。

**主要な操作・処理内容**：
1. 対象ユーザーの有効な偽装トークン一覧の表示
2. 新しい偽装トークンの作成（名前、説明、有効期限、スコープ設定）
3. 既存トークンの取り消し（Revoke）
4. 既存トークンのローテーション（有効期限を維持したまま新トークン発行）
5. トークンのスコープ（api, read_api等）の設定

**画面遷移**：
- 遷移元：ユーザー詳細画面、ユーザー一覧画面
- 遷移先：（同一画面内での操作が中心）

**権限による表示制御**：管理者権限を持つユーザーのみがアクセス可能です。偽装機能がシステムで無効化されている場合は、アクセス拒否となります。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 81 | 個人アクセストークン | 主機能 | 偽装トークンの作成・管理・取り消し |
| 111 | ユーザー管理 | 補助機能 | ユーザー情報の参照 |

## 画面種別

一覧 / 登録

## URL/ルーティング

- URL: `/admin/users/:user_id/impersonation_tokens`
- HTTPメソッド: GET（一覧）、POST（作成）、PUT（取り消し/ローテーション）
- コントローラー: `Admin::ImpersonationTokensController`

## 入出力項目

### 入力項目（トークン作成）

| 項目名 | 物理名 | 型 | 必須 | 説明 |
|--------|--------|-----|------|------|
| トークン名 | name | String | 必須 | トークンの識別名 |
| 説明 | description | String | - | トークンの用途説明 |
| 有効期限 | expires_at | Date | - | トークンの有効期限日 |
| スコープ | scopes | Array | 必須 | アクセス権限のスコープ |

### スコープ選択肢

| スコープ | 説明 |
|---------|------|
| api | API への完全なアクセス |
| read_api | API への読み取りアクセス |
| read_user | ユーザー情報の読み取り |
| read_repository | リポジトリの読み取り |
| write_repository | リポジトリへの書き込み |
| read_registry | コンテナレジストリの読み取り |
| write_registry | コンテナレジストリへの書き込み |
| sudo | sudo権限でのAPI実行 |

## 表示項目

### トークン一覧

| 項目名 | 説明 |
|--------|------|
| トークン名 | トークンの識別名 |
| スコープ | 付与されているスコープ一覧 |
| 作成日 | トークンの作成日時 |
| 有効期限 | トークンの有効期限 |
| 最終使用日 | トークンが最後に使用された日時 |
| アクション | 取り消し/ローテーションボタン |

### 新規作成後の表示

| 項目名 | 説明 |
|--------|------|
| トークン値 | 新しく発行されたトークン（一度だけ表示） |

## イベント仕様

### 1-トークン作成

**トリガー**: トークン作成フォームの送信

**処理フロー**:
1. Ajax POSTリクエストでトークン情報を送信
2. `PersonalAccessTokensFinder`でトークンを構築
3. `organization`を現在の組織に設定
4. トークンを保存
5. 成功時：新しいトークン値と更新されたトークン一覧をJSON返却
6. 失敗時：エラーメッセージをJSON返却

### 2-トークン取り消し

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

**処理フロー**:
1. PUT リクエストを `/admin/users/:user_id/impersonation_tokens/:id/revoke` に送信
2. `revoke!` メソッドでトークンを無効化
3. 成功時：成功メッセージをフラッシュに設定
4. 失敗時：エラーメッセージをフラッシュに設定
5. トークン一覧にリダイレクト

### 3-トークンローテーション

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

**処理フロー**:
1. PUT リクエストを `/admin/users/:user_id/impersonation_tokens/:id/rotate` に送信
2. `PersonalAccessTokens::RotateService` を呼び出し
3. 既存トークンの有効期限を維持（`keep_token_lifetime: true`）
4. 成功時：新しいトークン値と更新されたトークン一覧をJSON返却
5. 失敗時：エラーメッセージをJSON返却

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| トークン作成 | personal_access_tokens | INSERT | 新しい偽装トークンを作成 |
| トークン取り消し | personal_access_tokens | UPDATE | revokedフラグをtrueに設定 |
| トークンローテーション | personal_access_tokens | INSERT/UPDATE | 新トークン作成、旧トークン無効化 |
| 一覧表示 | personal_access_tokens | SELECT | 有効なトークンを取得 |

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

#### personal_access_tokens

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | 対象ユーザーのID | |
| INSERT | name | フォーム入力値 | |
| INSERT | description | フォーム入力値 | |
| INSERT | scopes | 選択されたスコープ | JSON配列形式 |
| INSERT | expires_at | フォーム入力値 | |
| INSERT | impersonation | true | 偽装トークンフラグ |
| INSERT | organization_id | Current.organization.id | |
| INSERT | token_digest | 生成されたトークンのハッシュ | |
| UPDATE | revoked | true | 取り消し時 |

## メッセージ仕様

| 種別 | 条件 | メッセージ |
|------|------|----------|
| 成功 | トークン取り消し成功 | "Revoked impersonation token {token_name}!" |
| エラー | トークン取り消し失敗 | "Could not revoke impersonation token {token_name}." |
| 情報 | PAT確認案内 | "To see all the user's personal access tokens you must impersonate them first." |

## 例外処理

| 状態 | 処理 |
|------|------|
| ユーザーが見つからない | 404エラー画面を表示 |
| 偽装機能が無効 | アクセス拒否画面にリダイレクト |
| 権限不足 | アクセス拒否画面にリダイレクト |
| バリデーションエラー | エラーメッセージをJSON返却（422） |
| ローテーション失敗 | エラーメッセージをJSON返却（422） |

## 備考

- トークン作成・取り消し・ローテーションはAjaxで処理される（ページリロード不要）
- トークン値は作成時・ローテーション時のみ表示され、以降は参照不可
- 偽装トークンは通常の個人アクセストークンとは別管理だが、同じテーブルを使用（`impersonation=true`で区別）
- `ImpersonationAccessTokenSerializer` を使用してトークン一覧をシリアライズ
- ユーザーの全ての個人アクセストークンを見るには、そのユーザーを偽装する必要がある

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | personal_access_token.rb | `app/models/personal_access_token.rb` | トークンモデルの属性、スコープ |
| 1-2 | impersonation_access_token_serializer.rb | `app/serializers/impersonation_access_token_serializer.rb` | JSON出力形式 |

**読解のコツ**: `impersonation`フラグで偽装トークンと通常トークンを区別。`scopes`配列でアクセス権限を管理。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | impersonation_tokens_controller.rb | `app/controllers/admin/impersonation_tokens_controller.rb` | CRUD操作の実装 |

**主要処理フロー**:
1. **行9-13**: `index` - トークン一覧とフォーム初期化
2. **行15-26**: `create` - 新規トークンの作成（Ajax）
3. **行28-39**: `revoke` - トークンの取り消し
4. **行41-53**: `rotate` - トークンのローテーション
5. **行63-64**: `verify_impersonation_enabled!` - 偽装機能の有効性チェック

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.html.haml | `app/views/admin/impersonation_tokens/index.html.haml` | 画面レイアウト |
| 3-2 | _form.html.haml | `app/views/shared/access_tokens/_form.html.haml` | トークン作成フォーム |

**主要処理フロー**:
- **行7-8**: ユーザー管理ヘッダーのレンダリング
- **行9**: `#js-new-access-token-app` - Vueマウントポイント
- **行11-34**: CRUDコンポーネントによるトークン管理UI

#### Step 4: サービスクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | personal_access_tokens_finder.rb | `app/finders/personal_access_tokens_finder.rb` | トークン検索ロジック |
| 4-2 | rotate_service.rb | `app/services/personal_access_tokens/rotate_service.rb` | ローテーションロジック |

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

```
Admin::ImpersonationTokensController
    │
    ├─ index
    │      ├─ set_index_vars
    │      │      ├─ Gitlab::Auth.available_scopes_for
    │      │      └─ active_impersonation_tokens
    │      └─ helpers.can_impersonate_user
    │
    ├─ create
    │      ├─ finder.build(params)
    │      ├─ @impersonation_token.save
    │      └─ active_impersonation_tokens
    │
    ├─ revoke
    │      ├─ finder.find(id)
    │      └─ @impersonation_token.revoke!
    │
    └─ rotate
           ├─ finder.find(id)
           └─ PersonalAccessTokens::RotateService
                  └─ execute(keep_token_lifetime: true)
```

### データフロー図

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

user_id ────────────▶ User.find_by!(username:) ────────▶ @user

params ─────────────▶ finder.build ─────────────────────▶ @impersonation_token
                            │
                            └─ impersonation: true
                            └─ organization: Current.organization

token_id ───────────▶ finder.find ──────────────────────▶ token
                            │
                            └─ revoke! / RotateService
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| impersonation_tokens_controller.rb | `app/controllers/admin/impersonation_tokens_controller.rb` | コントローラー | リクエスト処理 |
| index.html.haml | `app/views/admin/impersonation_tokens/index.html.haml` | テンプレート | 画面表示 |
| _form.html.haml | `app/views/shared/access_tokens/_form.html.haml` | パーシャル | トークン作成フォーム |
| personal_access_token.rb | `app/models/personal_access_token.rb` | モデル | トークンデータモデル |
| personal_access_tokens_finder.rb | `app/finders/personal_access_tokens_finder.rb` | Finder | トークン検索 |
| rotate_service.rb | `app/services/personal_access_tokens/rotate_service.rb` | サービス | ローテーションロジック |
| impersonation_access_token_serializer.rb | `app/serializers/impersonation_access_token_serializer.rb` | シリアライザー | JSON変換 |
| admin.rb | `config/routes/admin.rb` | ルーティング | URL定義 |
