# 機能設計書 32-APIトークン

## 概要

本ドキュメントは、JenkinsにおけるAPIトークン機能の設計を記載したものである。APIトークンはREST API認証用のトークン管理を担当し、ユーザーがパスワードを直接使用せずにAPIアクセスを行うための仕組みを提供する。

### 本機能の処理概要

本機能は、Jenkinsユーザーごとに一意のAPIトークンを生成・管理し、REST APIやCLIからの認証に使用できるようにする。

**業務上の目的・背景**：CI/CDパイプラインでは、スクリプトや自動化ツールからJenkinsのAPIを呼び出す必要が頻繁に発生する。パスワードを直接コードに埋め込むのはセキュリティリスクが高いため、APIトークンを使用することで、パスワードを露出させずに認証を実現する。また、トークンは個別に無効化できるため、漏洩時の影響を最小限に抑えられる。

**機能の利用シーン**：
- Jenkinsジョブをリモートから起動する自動化スクリプトの認証
- 外部システム（GitHub、GitLab等）からのWebhook認証
- jenkins-cli.jarを使用したCLIコマンドの認証
- CI/CDパイプラインからのJenkins API呼び出し
- 複数の用途に対して異なるトークンを使い分けたい場合

**主要な処理内容**：
1. 新規APIトークンの生成（ランダムなトークン値の生成とハッシュ保存）
2. トークンの検証（リクエストに含まれるトークンと保存済みハッシュの照合）
3. トークンの一覧表示と管理（名前変更、使用統計の表示）
4. トークンの無効化（revoke）
5. レガシートークンのサポートと移行

**関連システム・外部連携**：ApiTokenFilterを通じてHTTPリクエストのBasic認証を処理。REST API、CLI、Webhookなど様々なエントリーポイントからの認証に対応。

**権限による制御**：トークンの生成・管理はデフォルトで本人のみ可能。管理者がADMIN_CAN_GENERATE_NEW_TOKENSを有効にした場合のみ、管理者が他ユーザーのトークンを生成可能。レガシートークンの表示もSHOW_LEGACY_TOKEN_TO_ADMINSで制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 46 | ユーザーセキュリティ設定 | 主画面 | APIトークンの生成・管理 |

## 機能種別

認証処理 / トークン管理 / セキュリティ機能

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| newTokenName | String | No | 新規トークンの名前 | 空の場合は自動生成（作成日時） |
| tokenUuid | String | Yes（操作時） | 操作対象トークンのUUID | 空不可、有効なUUID形式 |
| newName | String | Yes（リネーム時） | トークンの新しい名前 | 空不可 |
| newTokenPlainValue | String | Yes（固定値追加時） | 固定のトークン値 | 空不可 |

### 入力データソース

- HTTPリクエスト: StaplerRequest2からフォームデータを取得
- User: 対象ユーザーオブジェクト（@AncestorInPath）
- ApiTokenPropertyConfiguration: トークン生成設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| tokenUuid | String | 生成されたトークンのUUID |
| tokenName | String | トークンの名前 |
| tokenValue | String | トークンの平文（生成時のみ表示） |
| tokenList | List<TokenInfoAndStats> | トークン一覧と統計情報 |

### 出力先

- JSON応答: トークン生成・操作結果
- ユーザー設定画面: トークン一覧の表示
- Userオブジェクト: トークン情報の永続化

## 処理フロー

### 処理シーケンス

```
1. トークン生成要求
   └─ doGenerateNewToken()がPOSTリクエストで呼び出される
2. 権限チェック
   └─ 本人または管理者（設定有効時）かを確認
3. ランダムトークン生成
   └─ ApiTokenStore.generateNewToken()で安全なランダム値を生成
4. ハッシュ化と保存
   └─ トークン値をハッシュ化してApiTokenStoreに保存
5. ユーザー保存
   └─ User.save()で永続化
6. 平文トークン返却
   └─ クライアントに平文トークンを1度だけ返却（以後は取得不可）
```

### フローチャート

```mermaid
flowchart TD
    A[トークン生成要求] --> B{権限チェック}
    B -->|権限なし| C[403 Forbidden]
    B -->|権限あり| D[トークン名決定]
    D --> E{名前指定あり?}
    E -->|Yes| F[指定名を使用]
    E -->|No| G[日時から自動生成]
    F --> H[ランダムトークン生成]
    G --> H
    H --> I[ハッシュ化して保存]
    I --> J[User.save]
    J --> K[平文トークンをJSONで返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-32-01 | 平文トークン一度きり | 生成時のみ平文トークンを表示、以後はハッシュのみ保存 | 常時 |
| BR-32-02 | 本人制限 | デフォルトでトークン管理は本人のみ可能 | ADMIN_CAN_GENERATE_NEW_TOKENS=false時 |
| BR-32-03 | 管理者生成 | ADMIN_CAN_GENERATE_NEW_TOKENSが有効な場合、管理者は他ユーザーのトークン生成可能 | 設定有効時 |
| BR-32-04 | レガシー非推奨 | レガシートークン（単一トークン）は非推奨、新方式（複数トークン）を推奨 | 常時 |
| BR-32-05 | 使用統計 | ApiTokenPropertyConfigurationで有効時、トークンの使用統計を記録 | 設定有効時 |

### 計算ロジック

トークンハッシュ生成: ランダムバイトをHMAC-SHA256でハッシュ化

## データベース操作仕様

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| トークン生成 | User XML | UPDATE | ユーザー設定ファイルにトークン情報を追加 |
| トークン削除 | User XML | UPDATE | ユーザー設定ファイルからトークン情報を削除 |
| 使用統計更新 | ApiTokenStats | UPDATE | 別ファイルに使用統計を保存 |

### テーブル別操作詳細

#### Userオブジェクト（XML永続化）

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|-----|-----------------|------|
| UPDATE | properties.ApiTokenProperty.tokenStore | トークンのハッシュ値リスト | 平文は保存しない |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | 権限のないユーザーがトークン操作を試行 | 本人でログインし直す |
| 400 | Bad Request | tokenUuidが空またはnull | 有効なUUIDを指定 |
| 400 | Bad Request | リネーム時に名前が空 | 有効な名前を指定 |
| 400 | Bad Request | トークンが存在しない | ページをリロードして再試行 |

### リトライ仕様

トークン操作はべき等ではないため、リトライ時は状態を確認してから再実行する必要がある。

## トランザクション仕様

User.save()により、ユーザー設定ファイル全体がアトミックに書き込まれる。トークン操作の途中で失敗した場合、変更は保存されない。

## パフォーマンス要件

- トークン検証は毎リクエストで実行されるため、高速な応答が必要
- ハッシュ比較はMessageDigest.isEqual()を使用して定時間比較（タイミング攻撃対策）

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

- トークンは平文で保存せず、ハッシュのみを保存
- 生成時のみ平文を表示（SECURITY-49対策）
- レガシートークンの管理者表示はデフォルト無効（SECURITY-200対策）
- 固定トークン追加機能（doAddFixedToken）は内部用途のみで慎重に使用
- トークンは個別に無効化可能で、漏洩時の影響を限定

## 備考

- ApiTokenPropertyはUserPropertyとしてユーザーごとに保持される
- API_KEY_SEEDはレガシートークン移行用で、新規システムでは使用しない
- トークン統計は別ファイル（ApiTokenStats）に保存され、設定同期ツールとの競合を回避

---

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

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

### 推奨読解順序

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

APIトークンのデータ構造とUserPropertyとしての配置を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ApiTokenProperty.java | `core/src/main/java/jenkins/security/ApiTokenProperty.java` | UserPropertyとしてのAPIトークン管理 |

**主要処理フロー**:
- **83-111行目**: クラス定義とフィールド（apiToken, tokenStore, tokenStats）
- **119-136行目**: setUser()でのトークンストア初期化
- **192-205行目**: matchesPassword()によるトークン検証ロジック
- **246-332行目**: TokenInfoAndStatsによるトークン情報と統計の結合

**読解のコツ**: ApiTokenPropertyはUserPropertyの実装であり、User.getProperty(ApiTokenProperty.class)で取得される。apiTokenはレガシー、tokenStoreが新方式。

#### Step 2: トークン生成と検証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ApiTokenProperty.java | `core/src/main/java/jenkins/security/ApiTokenProperty.java` | doGenerateNewToken()の処理フロー |

**主要処理フロー**:
- **569-595行目**: doGenerateNewToken() - トークン生成APIエンドポイント
- **434-438行目**: generateNewToken() - トークン生成の実装
- **458-468行目**: revokeToken() - トークン無効化

#### Step 3: 権限チェックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ApiTokenProperty.java | `core/src/main/java/jenkins/security/ApiTokenProperty.java` | canCurrentUserControlObject()による権限判定 |

**主要処理フロー**:
- **215-231行目**: canCurrentUserControlObject() - 本人または管理者の判定
- **94-107行目**: SHOW_LEGACY_TOKEN_TO_ADMINS, ADMIN_CAN_GENERATE_NEW_TOKENS設定

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

```
HTTP POST /user/{userId}/descriptorByName/.../generateNewToken
    │
    └─ DescriptorImpl.doGenerateNewToken()
           │
           ├─ hasCurrentUserRightToGenerateNewToken() 権限チェック
           │      └─ canCurrentUserControlObject()
           │
           ├─ ApiTokenProperty.generateNewToken()
           │      └─ ApiTokenStore.generateNewToken()
           │             └─ ランダムトークン生成
           │             └─ ハッシュ化して保存
           │
           └─ User.save() 永続化
```

### データフロー図

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

newTokenName ──────────▶ doGenerateNewToken() ──────────▶ JSON応答
                                │                         (tokenUuid,
User(@AncestorInPath) ─────────▶│                          tokenName,
                                │                          tokenValue)
                    ┌───────────┴───────────┐
                    ▼                       ▼
            権限チェック              トークン生成
                    │                       │
                    ▼                       ▼
           User.current()         ApiTokenStore
           Jenkins.ADMINISTER     .generateNewToken()
                                        │
                                        ▼
                               ハッシュ化して保存
                               (平文は返却のみ)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ApiTokenProperty.java | `core/src/main/java/jenkins/security/ApiTokenProperty.java` | ソース | APIトークンのUserProperty実装 |
| ApiTokenStore.java | `core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java` | ソース | トークンの保存・検証ロジック |
| ApiTokenStats.java | `core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java` | ソース | トークン使用統計の管理 |
| ApiTokenPropertyConfiguration.java | `core/src/main/java/jenkins/security/apitoken/ApiTokenPropertyConfiguration.java` | ソース | トークン機能の設定 |
| ApiTokenFilter.java | `core/src/main/java/jenkins/security/ApiTokenFilter.java` | ソース | HTTPリクエストからのトークン認証 |
| TokenUuidAndPlainValue.java | `core/src/main/java/jenkins/security/apitoken/TokenUuidAndPlainValue.java` | ソース | トークン生成結果の値オブジェクト |
