# 機能設計書 85-グループメンバー管理

## 概要

本ドキュメントは、GitLabにおけるグループメンバー管理機能の設計を記述する。グループオーナーおよび管理者は、グループへのメンバー追加、権限変更、削除を行うことができる。グループメンバーシップはサブグループおよび配下プロジェクトに継承される。

### 本機能の処理概要

**業務上の目的・背景**：企業やチームでは、複数のプロジェクトを束ねるグループ単位でのアクセス管理が効率的である。グループメンバー管理機能により、一度のメンバー追加で配下の全プロジェクトへのアクセス権を付与でき、管理負荷を大幅に削減できる。また、グループ階層構造により組織構造を反映したアクセス制御が可能となる。

**機能の利用シーン**：
- 新入社員をチームグループに追加し、全プロジェクトへのアクセス権を付与
- グループ内のメンバー一覧を確認し、権限を監査
- メンバーの退職時にグループから削除（配下プロジェクトからも自動削除）
- サブグループへの限定的なアクセス権付与
- 2要素認証の有効化状況を確認

**主要な処理内容**：
1. グループメンバーの一覧表示（継承メンバー含む）
2. 新規メンバーの追加（ユーザーID/ユーザー名/メール招待）
3. メンバーの権限レベル変更
4. メンバーの削除（サブグループ・プロジェクトからも削除）
5. 招待中メンバー一覧と管理
6. 2FAフィルタリング
7. ユーザータイプフィルタリング

**関連システム・外部連携**：
- プロジェクトメンバー管理（メンバーシップ継承）
- サブグループメンバー管理
- 通知システム
- LDAP同期（EE機能）

**権限による制御**：
- Ownerはすべてのメンバー操作が可能
- Maintainerは一部のメンバー操作が可能
- 最後のOwner削除は禁止（グループ管理不能になるため）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 144 | グループメンバー一覧 | 主画面 | グループメンバーの一覧表示 |

## 機能種別

CRUD操作 / 権限管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| user_id | Integer/Array | 条件付き | 追加するユーザーID | user_idまたはusernameが必須 |
| username | String/Array | 条件付き | 追加するユーザー名 | |
| access_level | Integer | Yes | 権限レベル（10-50） | 有効なアクセスレベルのみ |
| expires_at | Date | No | メンバーシップ有効期限 | 未来日付のみ |
| two_factor | String | No | 2FAフィルタ（enabled/disabled） | - |
| search | String | No | 検索クエリ | - |
| user_type | String | No | ユーザータイプフィルタ | - |

### 入力データソース

- 画面入力（Groups::GroupMembersController経由）
- API経由

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | メンバーID |
| user_id | Integer | ユーザーID |
| access_level | Integer | 権限レベル |
| expires_at | Date | 有効期限 |
| created_at | DateTime | 追加日時 |
| invited_user_state | String | 招待ユーザーの状態 |
| placeholder_users_count | Object | プレースホルダーユーザー数 |

### 出力先

- 画面表示（メンバー一覧、招待メンバー一覧）

## 処理フロー

### 処理シーケンス

```
1. メンバー一覧リクエスト受信
   └─ Groups::GroupMembersController#index
2. メンバー取得
   └─ GroupMembersFinder#execute
3. 招待メンバー取得（管理者のみ）
4. アクセスリクエスト取得
5. ページネーション適用
6. 結果返却
```

### フローチャート

```mermaid
flowchart TD
    A[メンバー一覧リクエスト] --> B{閲覧権限あり?}
    B -->|No| C[アクセス拒否]
    B -->|Yes| D[GroupMembersFinder実行]
    D --> E{管理権限あり?}
    E -->|Yes| F[招待メンバー取得]
    E -->|No| G[スキップ]
    F --> H[アクセスリクエスト取得]
    G --> H
    H --> I[present_members]
    I --> J[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-85-01 | 最後のOwner保護 | グループの最後のOwnerは削除できない | 削除時 |
| BR-85-02 | メンバーシップ継承 | グループメンバーは配下サブグループ・プロジェクトにアクセス可能 | 常時 |
| BR-85-03 | 削除時の連鎖 | グループメンバー削除時、配下のメンバーシップも削除 | 削除時 |
| BR-85-04 | ページネーション | メンバー一覧は50件/ページ | 一覧表示時 |

### 計算ロジック

**実効権限計算**：グループとプロジェクトで異なる権限がある場合、より高い権限が適用される。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 追加 | members | INSERT | source_type='Namespace'でINSERT |
| 削除 | members | DELETE | グループメンバー削除 |
| 削除連鎖 | members | DELETE | サブグループ・プロジェクトメンバー削除 |

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

#### members

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | source_id | グループID | |
| INSERT | source_type | 'Namespace' | GroupMemberはNamespace |
| DELETE | - | 対象レコード削除 | |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 最後のOwnerエラー | 最後のOwner削除試行 | 操作をブロック |
| - | 権限エラー | 管理権限なし | アクセス拒否 |

### リトライ仕様

特になし。

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

- グループオーナー削除時はロックを取得して実行（レースコンディション防止）

## パフォーマンス要件

- メンバー一覧取得：500ms以内

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

- 2FAフィルタリングによるセキュリティ監査支援
- 最後のOwner保護による管理権限喪失防止

## 備考

- MEMBER_PER_PAGE_LIMIT = 50
- プレースホルダーユーザー（インポート時の一時ユーザー）の管理機能あり

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | member.rb | `app/models/member.rb` | メンバー基底クラス |
| 1-2 | group_member.rb | `app/models/group_member.rb` | グループメンバー固有定義 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | group_members_controller.rb | `app/controllers/groups/group_members_controller.rb` | コントローラーアクション |

**主要処理フロー**:
- **10行目**: MEMBER_PER_PAGE_LIMIT = 50
- **27-47行目**: indexアクション
- **54-58行目**: GroupMembersFinder使用

#### Step 3: サービス層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | destroy_service.rb | `app/services/members/destroy_service.rb` | 削除ロジック（連鎖削除含む） |

**主要処理フロー**:
- **69-85行目**: process_destroy_of_group_owner_member - 最後のOwnerチェック
- **122-128行目**: delete_subresources - サブリソース削除
- **130-148行目**: delete_project_members, delete_subgroup_members, delete_invited_members

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

```
Groups::GroupMembersController#index
    │
    ├─ GroupMembersFinder#execute
    │      └─ include_relations適用
    │
    ├─ invited_members (管理者のみ)
    │
    ├─ AccessRequestsFinder#execute
    │
    └─ present_members

Groups::GroupMembersController#destroy (via MembershipActions)
    │
    └─ Members::DestroyService#execute
           ├─ process_destroy_of_group_owner_member (Ownerの場合)
           │      └─ last_owner?チェック + ロック
           ├─ destroy_member
           └─ delete_subresources
                  ├─ delete_project_members
                  ├─ delete_subgroup_members
                  └─ delete_invited_members
```

### データフロー図

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

フィルタパラメータ    GroupMembersFinder             画面表示
(search, two_factor, ──▶ ├─メンバー取得           ──▶  members一覧
 user_type)               ├─フィルタ適用
                          └─ページネーション
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| member.rb | `app/models/member.rb` | モデル | メンバー基底クラス |
| group_member.rb | `app/models/group_member.rb` | モデル | グループメンバー |
| group_members_controller.rb | `app/controllers/groups/group_members_controller.rb` | コントローラー | Web UI用 |
| destroy_service.rb | `app/services/members/destroy_service.rb` | サービス | 削除ロジック |
| group_members_finder.rb | `app/finders/group_members_finder.rb` | Finder | メンバー検索 |
