# 画面設計書 200-アクティブセッション一覧

## 概要

本ドキュメントは、アクティブセッション一覧画面の設計書である。ユーザーがログインしているすべてのデバイス・セッションを一覧表示し、不要なセッションを失効させる機能を提供する。

### 本画面の処理概要

ユーザーの現在アクティブなログインセッションを一覧表示する画面である。複数のデバイスやブラウザからログインしている場合、それぞれのセッション情報（IPアドレス、ブラウザ、OS、ログイン日時など）を確認でき、不審なセッションや不要なセッションを失効（ログアウト）させることができる。

**業務上の目的・背景**：セキュリティ上の理由から、ユーザーは自身のアカウントにログインしているすべてのセッションを把握する必要がある。第三者による不正アクセスが疑われる場合や、古いデバイスからのセッションを終了させたい場合に本画面を使用する。セッション管理はセキュリティのベストプラクティスとして重要な機能である。

**画面へのアクセス方法**：
1. 右上のユーザーアバターをクリック
2. 「設定」を選択
3. 左サイドバーから「アクティブセッション」を選択
4. または URL `/-/user_settings/active_sessions` に直接アクセス

**主要な操作・処理内容**：
1. アクティブセッションの一覧表示
2. 現在のセッションの識別
3. 個別セッションの失効（Revoke）

**画面遷移**：
- 遷移元: ユーザー設定メニュー
- 遷移先: なし（同一画面で更新）

**権限による表示制御**：
- ログイン必須
- なりすまし（impersonated）セッションは非表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 82 | セッション管理 | 主機能 | アクティブセッションの管理 |

## 画面種別

一覧

## URL/ルーティング

```
GET    /-/user_settings/active_sessions          # 一覧表示
DELETE /-/user_settings/active_sessions/:id      # セッション失効
```

## 入出力項目

### セッション失効

| 項目名 | 項目ID | 入出力 | 型 | 必須 | 説明 |
|--------|--------|--------|-----|------|------|
| セッションID | id | 入力 | string | Yes | 失効対象のセッションID（private_id） |

## 表示項目

### セッション一覧

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| デバイスアイコン | active_session.device_type | デバイス種別に応じたアイコン |
| デバイス種別 | active_session.human_device_type | Desktop, Smartphone等（ツールチップ表示） |
| IPアドレス | active_session.ip_address | 接続元IPアドレス |
| ブラウザ | active_session.browser | Chrome, Firefox等 |
| OS | active_session.os | Windows, macOS, Linux等 |
| ログイン日時 | active_session.created_at | セッション作成日時 |
| 最終アクセス日時 | active_session.updated_at | 最終アクティビティ日時 |
| 現在のセッションフラグ | is_current_session | 「This is your current session」表示 |
| Admin Mode | active_session.admin_mode | 管理者モードでのログイン |
| Step-up認証 | active_session.step_up_authenticated | ステップアップ認証済み |

## イベント仕様

### 1-セッション失効

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

**処理フロー**:
1. 確認ダイアログを表示（「Are you sure? The device will be signed out of GitLab and all remember me tokens revoked.」）
2. 確認後、DELETE リクエストを送信
3. `ActiveSession.destroy_session` でRedisからセッションを削除
4. `current_user.invalidate_all_remember_tokens!` で記憶トークンを無効化
5. セッション一覧画面にリダイレクト

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| セッション失効 | Redis（セッションストア） | DELETE | セッション情報の削除 |
| セッション失効 | users | UPDATE | remember_tokens の無効化 |

### Redisキー操作詳細

| 操作 | キー名 | 操作種別 | 備考 |
|-----|--------|---------|------|
| セッション失効 | `session:gitlab:v2:{user_id}:{session_id}` | DEL | セッションデータ削除 |
| セッション失効 | `session:gitlab:{session_id}` | DEL | Rackセッション削除 |
| セッション失効 | `session:lookup:user:gitlab:{user_id}` | SREM | ルックアップセットから削除 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG-001 | 情報 | This is your current session | 現在のセッションを表示時 |
| MSG-002 | 情報 | Last accessed on {date} | 非現在セッションの最終アクセス日時 |
| MSG-003 | 確認 | Are you sure? The device will be signed out of GitLab and all remember me tokens revoked. | 失効ボタン押下時 |
| MSG-004 | 情報 | with Admin Mode | 管理者モードでログイン時 |
| MSG-005 | 情報 | with Step-up Authentication | ステップアップ認証済み時 |

## 例外処理

| 例外 | 発生条件 | 対応処理 |
|-----|---------|---------|
| 未認証 | ログインしていない | ログイン画面へリダイレクト |
| セッション不存在 | 指定セッションが既に期限切れ/失効 | 正常終了（エラー表示なし） |

## 備考

- セッション情報はRedisに保存され、データベーステーブルではない
- セッションの有効期限は `Settings.gitlab['session_expire_delay']` 分で設定
- 最大アクティブセッション数は100件（`ALLOWED_NUMBER_OF_ACTIVE_SESSIONS`）
- 超過分は古いセッションから自動削除
- なりすまし（impersonated）セッションは一覧に表示されない
- セッションデータはv2フォーマット（JSON）で保存

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | active_session.rb | `app/models/active_session.rb` | セッションモデルの属性・Redisストレージ構造を確認 |

**読解のコツ**: `ActiveSession` はActiveRecordモデルではなく、`ActiveModel::Model` をincludeしたPlain Old Ruby Object（PORO）である。データはRedisに保存される。

**主要属性**（28-36行目）:
- `ip_address`, `browser`, `os`: クライアント情報
- `device_name`, `device_type`: デバイス識別
- `session_id`, `session_private_id`: セッション識別子
- `admin_mode`, `step_up_authenticated`: 認証状態
- `created_at`, `updated_at`: タイムスタンプ

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | active_sessions_controller.rb | `app/controllers/user_settings/active_sessions_controller.rb` | index/destroyアクションを確認 |

**主要処理フロー**:
1. **7-9行目**: `index` アクション - `ActiveSession.list(current_user)` でセッション一覧取得、なりすましセッションを除外
2. **11-19行目**: `destroy` アクション - セッション削除と記憶トークン無効化

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.html.haml | `app/views/user_settings/active_sessions/index.html.haml` | 一覧画面の構造確認 |
| 3-2 | _active_session.html.haml | `app/views/user_settings/active_sessions/_active_session.html.haml` | 個別セッション表示の詳細 |

**主要処理フロー**:
- **index.html.haml 11行目**: パーシャルを collection でレンダリング
- **_active_session.html.haml 1行目**: 現在セッションかどうかを判定
- **_active_session.html.haml 32-35行目**: 非現在セッションにのみ「Revoke」ボタン表示

#### Step 4: Redisストレージを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | active_session.rb | `app/models/active_session.rb` | Redis操作メソッドを確認 |

**主要メソッド**:
- **74-112行目**: `self.set` - 新規セッションの保存
- **114-120行目**: `self.list` - ユーザーのセッション一覧取得
- **149-155行目**: `self.destroy_session` - 単一セッションの削除
- **175-186行目**: Redisキー名の生成メソッド

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

```
UserSettings::ActiveSessionsController#index
    │
    └─ ActiveSession.list(current_user)
           │
           ├─ session_ids_for_user(user_id)
           │      └─ redis.smembers(lookup_key_name)
           │
           ├─ cleaned_up_lookup_entries(redis, user)
           │      └─ 期限切れセッションのクリーンアップ
           │
           └─ load_raw_session(raw_session)
                  └─ JSON.parse (v2形式)

UserSettings::ActiveSessionsController#destroy
    │
    ├─ ActiveSession.destroy_session(current_user, params[:id])
    │      │
    │      └─ destroy_sessions(redis, user, [session_id])
    │             ├─ redis.srem(lookup_key, session_ids)
    │             ├─ redis.del(key_names)
    │             └─ redis.del(session_keys)
    │
    └─ current_user.invalidate_all_remember_tokens!
           └─ users テーブル更新
```

### データフロー図

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

リクエスト ───▶ ActiveSessionsController#index
                      │
                      └─ ActiveSession.list(current_user)
                             │
                             ├─ Redis SMEMBERS
                             │      └─ session:lookup:user:gitlab:{user_id}
                             │
                             └─ Redis MGET
                                    └─ session:gitlab:v2:{user_id}:{session_id}
                                           │
                                           └─ [ActiveSession オブジェクト配列]

セッションID ───▶ ActiveSessionsController#destroy
                      │
                      ├─ ActiveSession.destroy_session
                      │      │
                      │      ├─ Redis SREM (lookup_key)
                      │      ├─ Redis DEL (session keys)
                      │      └─ Redis DEL (rack session keys)
                      │
                      └─ invalidate_all_remember_tokens!
                             └─ users.remember_tokens = nil
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| active_sessions_controller.rb | `app/controllers/user_settings/active_sessions_controller.rb` | コントローラー | リクエスト処理 |
| index.html.haml | `app/views/user_settings/active_sessions/index.html.haml` | テンプレート | 一覧画面 |
| _active_session.html.haml | `app/views/user_settings/active_sessions/_active_session.html.haml` | パーシャル | セッション項目表示 |
| active_session.rb | `app/models/active_session.rb` | モデル | セッションデータ定義・Redis操作 |
| user_settings.rb | `config/routes/user_settings.rb` | 設定 | ルーティング定義 |
| active_sessions_helper.rb | `app/helpers/active_sessions_helper.rb` | ヘルパー | デバイスアイコン表示等 |
