# 機能設計書 90-LDAP認証

## 概要

本ドキュメントは、GitLabにおけるLDAP認証機能の設計を記述する。組織のLDAPディレクトリ（Active Directory等）と連携し、ユーザーの認証・認可を一元管理する。OmniAuth LDAPを基盤として実装されている。

### 本機能の処理概要

**業務上の目的・背景**：企業環境では、Active Directory等のLDAPサーバーでユーザー情報を一元管理していることが多い。LDAP認証機能により、GitLabを既存のID管理基盤と統合し、ユーザーはLDAPの認証情報でGitLabにサインインできる。これにより、パスワード管理の負担軽減とセキュリティポリシーの統一が実現される。

**機能の利用シーン**：
- 企業のActive DirectoryアカウントでGitLabにサインイン
- OpenLDAPサーバーとの連携
- 複数LDAPサーバーの同時利用
- LDAPユーザーの自動プロビジョニング
- Active Directoryの無効化されたユーザーの自動ブロック

**主要な処理内容**：
1. LDAPサーバーへの認証バインド
2. ユーザー検索・認証
3. GitLabユーザーの作成・更新
4. アクセス権の同期
5. グループ同期（Enterprise Edition）
6. ユーザーブロック/アンブロック

**関連システム・外部連携**：
- LDAPサーバー（Active Directory、OpenLDAP等）
- OmniAuth LDAP
- Devise
- Net::LDAP gem

**権限による制御**：
- LDAP有効/無効は管理者設定
- サインイン制限は設定で制御
- 自動作成ユーザーのブロックは設定で制御

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 001 | サインイン | 主画面 | LDAPサインイン |
| 301 | 管理者画面 LDAP設定 | 設定画面 | LDAP設定管理 |

## 機能種別

認証・認可 / ディレクトリ連携

## 入力仕様

### 入力パラメータ（LDAP認証）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| username | String | Yes | LDAPユーザー名 | 必須 |
| password | String | Yes | LDAPパスワード | 必須 |
| provider | String | Yes | LDAPプロバイダ名 | 有効なプロバイダ |

### LDAP設定パラメータ

| パラメータ名 | 型 | 必須 | 説明 | デフォルト値 |
|-------------|-----|-----|------|--------------|
| host | String | Yes | LDAPサーバーホスト | - |
| port | Integer | Yes | LDAPポート | 389/636 |
| uid | String | Yes | ユーザー識別属性 | sAMAccountName |
| base | String | Yes | 検索ベースDN | - |
| bind_dn | String | No | バインドDN | - |
| password | String | No | バインドパスワード | - |
| encryption | String | No | 暗号化方式 | plain |
| verify_certificates | Boolean | No | 証明書検証 | true |
| active_directory | Boolean | No | AD互換モード | false |
| user_filter | String | No | ユーザーフィルタ | - |
| block_auto_created_users | Boolean | No | 自動作成ユーザーブロック | false |

### 入力データソース

- サインインフォーム
- LDAP管理設定画面

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| user | User | 認証されたユーザー |
| identity | Identity | LDAP連携情報（extern_uid=DN） |
| ldap_blocked | Boolean | LDAPブロック状態 |

### 出力先

- セッション確立
- 監査ログ

## 処理フロー

### 処理シーケンス

```
1. 認証リクエスト
   └─ Ldap::OmniauthCallbacksController#ldap
2. LDAPバインド
   └─ Gitlab::Auth::Ldap::Authentication#login
3. ユーザー検索
   └─ adapter.bind_as(filter, password)
4. GitLabユーザー検索・作成
   └─ Gitlab::Auth::Ldap::User#find_user
5. アクセスチェック
   └─ Gitlab::Auth::Ldap::Access.allowed?
6. サインイン処理
   └─ sign_in_user_flow
```

### フローチャート

```mermaid
flowchart TD
    A[LDAPサインイン] --> B{LDAP有効?}
    B -->|No| C[通常サインイン画面]
    B -->|Yes| D[認証情報入力]
    D --> E[LDAPバインド試行]
    E --> F{バインド成功?}
    F -->|No| G[認証失敗]
    G --> H[failed_attempts増加]
    H --> I[エラー表示]

    F -->|Yes| J[ユーザー検索]
    J --> K{GitLabユーザー存在?}
    K -->|Yes| L[既存ユーザー]
    K -->|No| M{自動作成許可?}
    M -->|No| N[SignupDisabledError]
    M -->|Yes| O[新規ユーザー作成]

    L --> P{Active Directory?}
    O --> P
    P -->|No| Q[アクセス許可]
    P -->|Yes| R{AD無効化?}
    R -->|Yes| S[ldap_block]
    R -->|No| T{LDAPブロック中?}
    T -->|Yes| U[activate]
    T -->|No| Q

    S --> I
    U --> Q
    Q --> V{2FA有効?}
    V -->|Yes| W[2FAプロンプト]
    V -->|No| X[サインイン完了]
    W --> Y{2FA成功?}
    Y -->|Yes| X
    Y -->|No| I
    X --> Z[監査ログ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-90-01 | LDAP有効化 | ldap.enabled設定でLDAP認証を有効化 | 常時 |
| BR-90-02 | サインイン制限 | prevent_ldap_sign_in設定でサインインを制限可能 | 常時 |
| BR-90-03 | 自動ブロック | Active Directory無効ユーザーは自動的にブロック | active_directory: true |
| BR-90-04 | 自動アンブロック | LDAP復活ユーザーは自動的にアンブロック | LDAPブロック状態時 |
| BR-90-05 | ユーザー非存在ブロック | LDAPに存在しなくなったユーザーはブロック | 常時 |
| BR-90-06 | 自動作成ブロック | block_auto_created_users設定で新規ユーザーをブロック | 設定有効時 |
| BR-90-07 | 複数サーバー | 複数LDAPサーバーを順次試行 | 複数設定時 |
| BR-90-08 | SSH鍵同期 | sync_ssh_keys属性でSSH鍵を同期 | 設定有効時 |

### 計算ロジック

**ユーザーフィルタ構築**：`Net::LDAP::Filter.equals(uid, username)`を基本とし、user_filter設定がある場合は結合。

**DN正規化**：`Person.normalize_dn`でDNを正規化して比較。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ユーザー検索 | identities | SELECT | DN/provider検索 |
| 新規作成 | users | INSERT | LDAPユーザー作成 |
| 新規作成 | identities | INSERT | LDAP連携作成 |
| ブロック | users | UPDATE | state変更 |
| 更新 | users | UPDATE | last_credential_check_at |

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

#### identities

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | provider | ldap* | |
| SELECT | extern_uid | DN | |
| INSERT | user_id | ユーザーID | |
| INSERT | provider | ldap* | |
| INSERT | extern_uid | DN | |

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | LDAP cn属性 | |
| INSERT | username | LDAP uid属性 | |
| INSERT | email | LDAP mail属性 | |
| UPDATE | state | ldap_blocked | AD無効時 |
| UPDATE | state | active | 復活時 |
| UPDATE | last_credential_check_at | 現在日時 | 認証成功時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | LdapConnectionError | LDAP接続失敗 | サーバー設定確認 |
| - | InvalidProvider | 無効なプロバイダ | 設定確認 |
| - | SignupDisabledError | 自動作成無効 | 管理者に連絡 |
| - | 認証失敗 | パスワード不正 | "Access denied"表示 |

### リトライ仕様

- 複数LDAPサーバー設定時は順次試行
- retry_empty_result_with_codes設定で空結果時のリトライ

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

ユーザー作成とアイデンティティ作成は同一トランザクション内で実行。

## パフォーマンス要件

- LDAP認証：timeout設定で制御（デフォルト10秒）
- ユーザー検索：size: 1で結果を制限

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

- パスワードは暗号化ファイル（secret_file）で管理可能
- TLS/SSL暗号化（simple_tls、start_tls）サポート
- 証明書検証設定
- バインドDN/パスワードの安全な管理
- 監査ログによる認証イベント追跡

## 備考

- NET_LDAP_ENCRYPTION_METHOD = { simple_tls: :simple_tls, start_tls: :start_tls, plain: nil }
- デフォルト属性マッピング: username=uid/sAMAccountName, email=mail, name=cn
- EE機能：グループ同期、SSH鍵同期、管理者グループ設定

---

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

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

### 推奨読解順序

#### Step 1: 設定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | config.rb | `lib/gitlab/auth/ldap/config.rb` | LDAP設定管理 |

**主要処理フロー**:
- **8-12行目**: NET_LDAP_ENCRYPTION_METHOD定義
- **18-28行目**: enabled?, sign_in_enabled?, prevent_ldap_sign_in?
- **30-32行目**: serversメソッド
- **44-49行目**: providers, available_providers
- **69-77行目**: initializeメソッド
- **83-92行目**: adapter_options（Net::LDAP接続オプション）
- **94-112行目**: omniauth_options
- **114-124行目**: base, uid, label設定取得
- **135-141行目**: user_filter, constructed_user_filter
- **155-158行目**: active_directory設定
- **167-169行目**: block_auto_created_users
- **171-173行目**: attributes（属性マッピング）
- **203-213行目**: encryption設定
- **215-223行目**: default_attributes

#### Step 2: 認証処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | authentication.rb | `lib/gitlab/auth/ldap/authentication.rb` | 認証処理 |

**主要処理フロー**:
- **12行目**: Gitlab::Auth::OAuth::Authenticationを継承
- **13-22行目**: self.loginクラスメソッド（プロバイダ順次試行）
- **24-26行目**: self.providers
- **28-37行目**: loginインスタンスメソッド
- **39-41行目**: adapterメソッド（OmniAuth::LDAP::Adaptor）
- **43-45行目**: configメソッド
- **47-69行目**: user_filterメソッド

#### Step 3: アクセス制御を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | access.rb | `lib/gitlab/auth/ldap/access.rb` | アクセス制御 |

**主要処理フロー**:
- **13-17行目**: self.openメソッド
- **19-34行目**: self.allowed?メソッド（許可判定+更新）
- **43-65行目**: allowed?インスタンスメソッド
- **45-48行目**: 非AD時の処理
- **50-57行目**: AD無効化チェック
- **59-61行目**: LDAP非存在時ブロック
- **91-105行目**: block_userメソッド
- **107-114行目**: unblock_userメソッド

#### Step 4: ユーザー処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | user.rb | `lib/gitlab/auth/ldap/user.rb` | LDAPユーザー処理 |

**主要処理フロー**:
- **12行目**: Gitlab::Auth::OAuth::Userを継承
- **16-18行目**: find_userメソッド
- **25-27行目**: block_after_signup?
- **29-31行目**: allowed?メソッド
- **33-39行目**: valid_sign_in?（super + allowed?）
- **41-43行目**: ldap_configメソッド
- **45-47行目**: auth_hash=（LdapAuthHash使用）

#### Step 5: コントローラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | omniauth_callbacks_controller.rb | `app/controllers/ldap/omniauth_callbacks_controller.rb` | LDAPコールバック |

**主要処理フロー**:
- **3行目**: OmniauthCallbacksControllerを継承
- **8-14行目**: define_providers!（プロバイダ毎のエイリアス）
- **18-26行目**: ldapメソッド（メイン処理）
- **30-33行目**: set_remember_meオーバーライド
- **35-40行目**: fail_loginオーバーライド
- **44-52行目**: available_providers
- **54-57行目**: log_audit_event（'authenticated_with_ldap'）

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

```
Ldap::OmniauthCallbacksController#ldap
    │
    ├─ Gitlab::Auth::Ldap::Config.sign_in_enabled?
    │
    ├─ admin_mode_request? → admin_mode_flow
    │
    └─ sign_in_user_flow(Gitlab::Auth::Ldap::User)
           │
           ├─ build_auth_user(Gitlab::Auth::Ldap::User)
           │      └─ Gitlab::Auth::Ldap::User.new(oauth)
           │
           ├─ auth_user.find_and_update!
           │      ├─ find_user
           │      │      ├─ find_by_uid_and_provider (DN)
           │      │      ├─ find_by_email
           │      │      └─ build_new_user
           │      └─ save
           │
           ├─ auth_user.valid_sign_in?
           │      └─ allowed?
           │             └─ Gitlab::Auth::Ldap::Access.allowed?(gl_user)
           │                    ├─ find_ldap_user
           │                    ├─ disabled_via_active_directory?
           │                    ├─ block_user / unblock_user
           │                    └─ update_user (EE)
           │
           └─ sign_in_and_redirect

Gitlab::Auth::Ldap::Authentication.login(login, password)
    │
    ├─ providers.find do |provider|
    │      └─ new(provider).login(login, password)
    │
    └─ login instance method
           ├─ adapter.bind_as(filter, password)
           │      └─ OmniAuth::LDAP::Adaptor
           │             └─ Net::LDAP
           │
           └─ Gitlab::Auth::Ldap::User.find_by_uid_and_provider(dn, provider)
```

### データフロー図

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

サインインフォーム     Ldap::OmniauthCallbacksController  セッション
(username,        ──▶  ├─LDAP有効確認               ──▶  確立
 password)             │
                       ├─OmniAuth LDAP認証
                       │   └─adapter.bind_as
                       │         └─Net::LDAP
                       │               └─LDAPサーバー
                       │
                       ├─Gitlab::Auth::Ldap::User
                       │   ├─find_by_uid_and_provider(DN)
                       │   └─build_new_user
                       │
                       ├─Gitlab::Auth::Ldap::Access.allowed?
                       │   ├─find_ldap_user
                       │   ├─disabled_via_active_directory?
                       │   └─block_user / unblock_user
                       │
                       └─sign_in_and_redirect
                                                      ▼
                                                   リダイレクト
                                                   監査ログ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| config.rb | `lib/gitlab/auth/ldap/config.rb` | ライブラリ | LDAP設定 |
| authentication.rb | `lib/gitlab/auth/ldap/authentication.rb` | ライブラリ | 認証処理 |
| access.rb | `lib/gitlab/auth/ldap/access.rb` | ライブラリ | アクセス制御 |
| user.rb | `lib/gitlab/auth/ldap/user.rb` | ライブラリ | ユーザー処理 |
| omniauth_callbacks_controller.rb | `app/controllers/ldap/omniauth_callbacks_controller.rb` | コントローラー | LDAPコールバック |
| adapter.rb | `lib/gitlab/auth/ldap/adapter.rb` | ライブラリ | LDAPアダプタ |
| person.rb | `lib/gitlab/auth/ldap/person.rb` | ライブラリ | LDAPユーザー情報 |
| auth_hash.rb | `lib/gitlab/auth/ldap/auth_hash.rb` | ライブラリ | 認証ハッシュ |
| identity.rb | `app/models/identity.rb` | モデル | アイデンティティ |
