# 機能設計書 30-ユーザー管理

## 概要

本ドキュメントは、Jenkinsのユーザー管理（User）機能の設計仕様を定義する。Userクラスは、Jenkinsシステム内のユーザーを表現し、ユーザー情報の管理、認証との連携、ビルド参加者の追跡などの機能を提供する。

### 本機能の処理概要

ユーザー管理機能は、Jenkinsにおけるユーザーの作成、読み込み、更新、削除（CRUD）と、ユーザープロパティの管理、ビルドとの関連付けを行う機能である。

**業務上の目的・背景**：Jenkinsでは、認証済みユーザーの管理、SCMコミッターとJenkinsユーザーの紐付け、ユーザー固有の設定（API Token、メールアドレス等）の管理が必要である。Userクラスは、これらの要件を満たすための中核的なデータモデルを提供する。

**機能の利用シーン**：
- ユーザーログイン時のユーザーオブジェクト取得
- SCMコミットからのユーザー特定
- ユーザー設定ページでの情報編集
- API Tokenの管理
- ビルド参加者の追跡

**主要な処理内容**：
1. ユーザーの取得（getById、get、current）
2. ユーザー情報の永続化（save、load）
3. ユーザープロパティの管理（addProperty）
4. ユーザーの削除（delete）
5. ユーザー認証の偽装（impersonate）
6. ビルド参加履歴の取得（getBuilds）

**関連システム・外部連携**：SecurityRealm（認証）、SCM（コミッター情報）、UserProperty（拡張プロパティ）、JNLP Agent等。

**権限による制御**：ユーザーは自分自身の情報にフルアクセス可能。他ユーザーの情報へのアクセスはAdminister権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | ユーザー一覧 | 主画面 | ユーザー一覧の表示 |
| 6 | ユーザー詳細 | 詳細画面 | ユーザー情報の表示 |
| 7 | ユーザー設定 | 設定画面 | ユーザー情報の編集 |

## 機能種別

データモデル / ユーザー管理

## 入力仕様

### 入力パラメータ（getById）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | String | Yes | ユーザーID | null不可、空文字不可 |
| create | boolean | Yes | 存在しない場合に作成するか | true/false |

### 入力パラメータ（get）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| idOrFullName | String | Yes | ユーザーIDまたはフルネーム | null不可 |
| create | boolean | Yes | 存在しない場合に作成するか | true/false |
| context | Map | No | 解決コンテキスト | nullの場合は空Map |

### 入力データソース

- ユーザー設定画面からの入力
- SCMからのコミッター情報
- 認証システムからのユーザー情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| User | User | ユーザーオブジェクト |
| properties | Map<Descriptor, UserProperty> | ユーザープロパティ |
| builds | RunList | 関連ビルドのリスト |

### 出力先

- ファイルシステム（$JENKINS_HOME/users/[hash]_[id]/config.xml）
- 画面表示

## 処理フロー

### 処理シーケンス

```
1. ユーザー取得リクエスト
   └─ User.getById(id, create) 呼び出し
2. AllUsersから既存ユーザー検索
   └─ IdStrategyに基づいたキー変換
3. 既存ユーザーなしの場合
   └─ createがtrueなら新規作成
   └─ config.xmlから読み込み試行
   └─ デフォルトプロパティの割り当て
4. ユーザーオブジェクト返却
```

### フローチャート

```mermaid
flowchart TD
    A[getById呼び出し] --> B{AllUsersに存在?}
    B -->|Yes| C[既存Userを返却]
    B -->|No| D{create=true?}
    D -->|Yes| E[新規User作成]
    D -->|No| F[null返却]
    E --> G{config.xml存在?}
    G -->|Yes| H[config.xmlから読み込み]
    G -->|No| I[空のUser作成]
    H --> J[デフォルトプロパティ割り当て]
    I --> J
    J --> K[AllUsersに登録]
    K --> L[User返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 禁止ユーザー名 | anonymous、system、unknownはユーザー作成不可 | save()時 |
| BR-002 | 自己アクセス権限 | 非匿名ユーザーは自分自身にフルアクセス権限を持つ | getACL()時 |
| BR-003 | ID Strategy | SecurityRealmのIdStrategyに従ってユーザーIDを処理 | 全ユーザー検索時 |
| BR-004 | オンデマンド作成 | ユーザーは必要に応じてオンデマンドで作成される | get()時 |

### 計算ロジック

```java
// ユーザーIDからディレクトリ名の計算
private static String getUserFolderNameFor(String id) {
    var fullPrefix = DISALLOWED_PREFIX_CHARS.matcher(id)
        .replaceAll("").toLowerCase(Locale.ROOT);
    return (fullPrefix.length() > PREFIX_MAX ?
            fullPrefix.substring(0, PREFIX_MAX) : fullPrefix) +
           '_' + DIRNAMES.mac(idStrategy().keyFor(id));
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ユーザー保存 | config.xml | INSERT/UPDATE | ユーザー設定の永続化 |
| ユーザー削除 | ディレクトリ全体 | DELETE | ユーザーディレクトリの削除 |

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

#### config.xml（ユーザー設定ファイル）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | id、fullName、description、properties | ユーザー情報 | XStreamシリアライズ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | FormValidation | 禁止ユーザー名での保存試行 | エラーメッセージ表示 |
| - | UsernameNotFoundException | 存在しないユーザーでの偽装試行 | 例外送出 |
| - | IOException | config.xml読み書きエラー | ログ記録、処理続行 |

### リトライ仕様

config.xml読み込みエラー時のリトライは行わない。エラーはログに記録され、空のUserオブジェクトが返される。

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

トランザクション管理はない。ユーザー情報の変更はファイルへの書き込みで永続化される。

## パフォーマンス要件

- AllUsersはConcurrentMapで管理され、高速なルックアップを提供
- ユーザー検索はIdStrategyに基づくハッシュキーで実行
- 大量のユーザーがいる環境でもO(1)でのアクセスを保証

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

- ユーザーディレクトリ名はハッシュ化されて保存
- 禁止ユーザー名（anonymous、system、unknown）での作成を防止
- ALLOW_NON_EXISTENT_USER_TO_LOGINフラグで認証バイパスを制御
- ALLOW_USER_CREATION_VIA_URLフラグでURL経由のユーザー作成を制御

## 備考

- Userオブジェクトはシングルトンパターンで管理（同一IDに対して同一インスタンス）
- SCMコミットログからのユーザー特定にはCanonicalIdResolverが使用される
- UserPropertyによる拡張機能（API Token、SSHキー等）のサポート

---

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

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

### 推奨読解順序

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

Userクラスの基本構造とフィールドを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | User.java | `core/src/main/java/hudson/model/User.java` | クラスフィールド（id、fullName、description、properties） |

**読解のコツ**: Userは複数のインターフェースを実装（AccessControlled、Saveable、Loadable等）。ConcurrentMapでシングルトン管理。

#### Step 2: ユーザー取得メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | User.java | `core/src/main/java/hudson/model/User.java` | getById()、get()、current() |

**主要処理フロー**:
- **127行目**: User - クラス定義（ExportedBean）
- **686-688行目**: getById() - IDによるユーザー取得
- **563-575行目**: get() - ID/フルネームによるユーザー取得
- **646-648行目**: current() - 現在ログインユーザー取得
- **658-664行目**: get2(Authentication) - 認証情報からユーザー取得

#### Step 3: 永続化処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | User.java | `core/src/main/java/hudson/model/User.java` | save()、load()、delete() |

**主要処理フロー**:
- **885-898行目**: save() - ユーザー設定の保存
- **202-216行目**: load() - ユーザー設定の読み込み
- **905-910行目**: delete() - ユーザーの削除

#### Step 4: ユーザープロパティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | User.java | `core/src/main/java/hudson/model/User.java` | addProperty()、getProperty()、getAllProperties() |

**主要処理フロー**:
- **355-364行目**: addProperty() - プロパティの追加
- **404-410行目**: getProperty() - プロパティの取得
- **392-399行目**: getAllProperties() - 全プロパティの取得

#### Step 5: 認証連携を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | User.java | `core/src/main/java/hudson/model/User.java` | impersonate2()、getUserDetailsForImpersonation2() |

**主要処理フロー**:
- **423-425行目**: impersonate2() - ユーザー認証の偽装
- **450-471行目**: getUserDetailsForImpersonation2() - 偽装用UserDetails取得
- **505-507行目**: impersonate(UserDetails) - Authentication生成

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

```
ユーザー取得
    │
    ├─ User.getById(id, create)
    │      │
    │      └─ AllUsers.get(id)
    │             │
    │             ├─ [既存] User返却
    │             │
    │             └─ [新規] new User(id, fullName)
    │                    │
    │                    ├─ load(id)
    │                    │      └─ XmlFile.unmarshal()
    │                    │
    │                    └─ allocateDefaultPropertyInstances()
    │
    └─ User.get(idOrFullName, create, context)
           │
           └─ CanonicalIdResolver.resolve()
                  │
                  └─ getOrCreateById()

ユーザー保存
    │
    └─ User.save()
           │
           ├─ isIdOrFullnameAllowed()チェック
           │
           ├─ XmlFile.write()
           │
           └─ SaveableListener.fireOnChange()

ユーザー認証偽装
    │
    └─ User.impersonate2()
           │
           └─ getUserDetailsForImpersonation2()
                  │
                  ├─ ImpersonatingUserDetailsService2.loadUserByUsername()
                  │
                  └─ [例外時] LegitimateButUnknownUserDetails
```

### データフロー図

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

ユーザーID           User.getById()
├─ id              ├─ AllUsers検索 ────────────▶     User
├─ create          ├─ config.xml読み込み
└─ context         └─ プロパティ割り当て

User設定             User.save()
├─ fullName        ├─ バリデーション ──────────▶     config.xml
├─ description     └─ XmlFile.write()
└─ properties

Authentication      User.impersonate2()
(偽装対象)         ├─ SecurityRealm問い合わせ ──▶     Authentication
                   └─ UserDetails取得
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| User.java | `core/src/main/java/hudson/model/User.java` | ソース | ユーザーモデルクラス |
| UserProperty.java | `core/src/main/java/hudson/model/UserProperty.java` | ソース | ユーザープロパティ基底クラス |
| UserPropertyDescriptor.java | `core/src/main/java/hudson/model/UserPropertyDescriptor.java` | ソース | ユーザープロパティDescriptor |
| CanonicalIdResolver.java | `core/src/main/java/hudson/model/User.java` | ソース内部クラス | ユーザーID解決 |
| AllUsers.java | `core/src/main/java/hudson/model/User.java` | ソース内部クラス | ユーザーキャッシュ管理 |
| IdStrategy.java | `core/src/main/java/jenkins/model/IdStrategy.java` | ソース | ID変換戦略 |
| ImpersonatingUserDetailsService2.java | `core/src/main/java/jenkins/security/ImpersonatingUserDetailsService2.java` | ソース | 偽装用UserDetailsService |
