# 機能設計書 67-セッションデータ永続化

## 概要

本ドキュメントは、Etherpadのセッションデータ永続化機能の設計を記載する。この機能は、著者とグループ間のAPIセッション情報をueberDB2を介してデータベースに保存する。

### 本機能の処理概要

セッションデータ永続化機能は、EtherpadのAPI経由で作成されるセッション（著者とグループの関連付け）を管理・保存する機能である。セッションは有効期限を持ち、グループパッドへのアクセス制御に使用される。

**業務上の目的・背景**：Etherpadでグループパッドを使用する場合、誰がどのグループにアクセスできるかを制御する必要がある。セッションを通じて、特定の著者に特定のグループへのアクセス権を期限付きで付与できる。これにより、外部システムとの連携や一時的なアクセス権付与が可能となる。

**機能の利用シーン**：
- 外部システムからAPIでセッションを作成する時
- グループパッドにアクセスする時（セッション検証）
- セッションを削除してアクセス権を取り消す時
- 著者またはグループに関連するセッション一覧を取得する時

**主要な処理内容**：
1. セッションの作成（createSession）
2. セッション情報の取得（getSessionInfo）
3. セッションの削除（deleteSession）
4. グループまたは著者に関連するセッション一覧取得
5. セッションIDから著者IDを検索（findAuthorID）

**関連システム・外部連携**：
- ueberDB2（データベース抽象化層）
- グループ管理（グループの存在確認）
- 著者管理（著者の存在確認）
- セキュリティマネージャー（アクセス制御）

**権限による制御**：セッションの作成・削除はAPI経由でのみ可能。セッションの有効性はvalidUntilタイムスタンプで制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | （直接的な画面関連なし） | API | REST API経由でのセッション管理 |

## 機能種別

データ永続化 / CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| groupID | string | Yes | グループの識別子 | g.で始まる文字列 |
| authorID | string | Yes | 著者の識別子 | a.で始まる文字列 |
| validUntil | number | Yes | 有効期限（UNIX秒） | 正の整数、未来の日時 |
| sessionID | string | Yes（取得・削除時） | セッションの識別子 | s.で始まる文字列 |

### 入力データソース

- REST API（セッション管理API）
- 内部呼び出し（アクセス制御検証時）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| session:{sessionID} | object | セッション情報オブジェクト |
| group2sessions:{groupID} | object | グループに関連するセッションIDマップ |
| author2sessions:{authorID} | object | 著者に関連するセッションIDマップ |

### 出力先

- ueberDB2を介したデータベース

## 処理フロー

### 処理シーケンス

```
1. セッション作成リクエスト
   └─ createSession(groupID, authorID, validUntil)
2. 事前チェック
   └─ グループ存在確認
   └─ 著者存在確認
   └─ validUntilの検証
3. セッションID生成
   └─ s. + ランダム16文字
4. セッションレコード保存
   └─ session:{sessionID} に保存
5. 関連レコード更新
   └─ group2sessions:{groupID} にセッションID追加
   └─ author2sessions:{authorID} にセッションID追加
```

### フローチャート

```mermaid
flowchart TD
    A[createSession呼び出し] --> B{グループ存在?}
    B -->|No| C[エラー: groupID does not exist]
    B -->|Yes| D{著者存在?}
    D -->|No| E[エラー: authorID does not exist]
    D -->|Yes| F{validUntil検証}
    F -->|無効| G[エラー: 各種検証エラー]
    F -->|有効| H[sessionID生成]
    H --> I[session:{sessionID}保存]
    I --> J[group2sessions更新]
    J --> K[author2sessions更新]
    K --> L[sessionID返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-67-01 | セッションID形式 | セッションIDは"s."プレフィックス + 16文字のランダム文字列 | セッション作成時 |
| BR-67-02 | 有効期限チェック | validUntilは未来の日時でなければならない | セッション作成時 |
| BR-67-03 | 有効期限形式 | validUntilは正の整数でなければならない | セッション作成時 |
| BR-67-04 | セッション削除順序 | セッション削除時は関連レコードを先に更新 | セッション削除時 |

### 計算ロジック

セッションID生成:
```typescript
const sessionID = `s.${randomString(16)}`;
```

有効期限チェック:
```typescript
if (validUntil < Math.floor(Date.now() / 1000)) {
  throw new CustomError('validUntil is in the past', 'apierror');
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| セッション作成 | session:{sessionID} | INSERT | セッション情報の保存 |
| グループ関連付け | group2sessions:{groupID} | UPDATE | セッションIDの追加 |
| 著者関連付け | author2sessions:{authorID} | UPDATE | セッションIDの追加 |
| セッション削除 | session:{sessionID} | DELETE | セッション情報の削除 |
| 関連削除 | group2sessions/author2sessions | UPDATE | セッションIDの削除 |

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

#### session:{sessionID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | groupID | string | 関連グループID |
| INSERT | authorID | string | 関連著者ID |
| INSERT | validUntil | number | 有効期限（UNIX秒） |

#### group2sessions:{groupID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | sessionIDs.{sessionID} | 1 or undefined | 追加時は1、削除時はundefined |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| apierror | グループ不存在 | groupIDが存在しない | エラーメッセージを返却 |
| apierror | 著者不存在 | authorIDが存在しない | エラーメッセージを返却 |
| apierror | 無効な有効期限 | validUntilが過去または無効 | エラーメッセージを返却 |
| apierror | セッション不存在 | sessionIDが存在しない | エラーメッセージを返却 |

### リトライ仕様

エラー時は自動リトライなし。

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

セッション作成・削除時は複数のレコードを更新するが、ueberDB2のsetSub()によるアトミック更新で整合性を維持。

## パフォーマンス要件

- セッション検証: リアルタイムのアクセス制御に影響しない速度

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

- セッションIDはランダム生成で推測困難
- 有効期限による自動失効

## 備考

- セッションはグループパッドへのアクセス制御に使用
- セッションCookieとの関連付けはセキュリティマネージャーで処理

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SessionManager.ts | `src/node/db/SessionManager.ts` | セッション情報の構造 |

**読解のコツ**: セッションは{groupID, authorID, validUntil}の3つの属性を持つ。

#### Step 2: セッション作成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SessionManager.ts | `src/node/db/SessionManager.ts` | createSession関数（105-159行目） |

**主要処理フロー**:
1. **107-116行目**: グループ・著者の存在確認
2. **118-141行目**: validUntilの検証
3. **143-144行目**: セッションID生成
4. **146-147行目**: session:{sessionID}の保存
5. **149-156行目**: group2sessions/author2sessionsの更新

#### Step 3: セッション削除処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SessionManager.ts | `src/node/db/SessionManager.ts` | deleteSession関数（184-206行目） |

**主要処理フロー**:
- **186-189行目**: セッション存在確認
- **195-201行目**: group2sessions/author2sessionsからの削除
- **205行目**: session:{sessionID}の削除

#### Step 4: セッション検索処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SessionManager.ts | `src/node/db/SessionManager.ts` | findAuthorID関数（39-85行目） |
| 4-2 | SessionManager.ts | `src/node/db/SessionManager.ts` | listSessionsOfGroup/Author関数 |

**主要処理フロー**:
- **39-85行目**: findAuthorID関数 - セッションCookieから著者ID検索
- **213-222行目**: listSessionsOfGroup関数
- **229-237行目**: listSessionsOfAuthor関数

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

```
REST API（セッション作成）
    │
    └─ sessionManager.createSession(groupID, authorID, validUntil)
           │
           ├─ groupManager.doesGroupExist(groupID)
           ├─ authorManager.doesAuthorExist(authorID)
           │
           ├─ randomString(16) → sessionID生成
           │
           ├─ db.set(`session:${sessionID}`, {...})
           │
           └─ db.setSub(`group2sessions:${groupID}`, ...)
           └─ db.setSub(`author2sessions:${authorID}`, ...)

SecurityManager（アクセス検証）
    │
    └─ sessionManager.findAuthorID(groupID, sessionCookie)
           │
           ├─ セッションCookieの解析
           │
           └─ getSessionInfo(sessionID) × N
                  │
                  └─ 有効期限チェック
```

### データフロー図

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

APIリクエスト ───▶ createSession()
                         │
         ┌───────────────┼───────────────┐
         │               │               │
         ▼               ▼               ▼
   グループ確認    著者確認    validUntil検証
         │               │               │
         └───────────────┼───────────────┘
                         │
                         ▼
                 sessionID生成
                         │
         ┌───────────────┼───────────────┐
         │               │               │
         ▼               ▼               ▼
  session保存   group2sessions   author2sessions
         │               │               │
         └───────────────┼───────────────┘
                         │
                         ▼
                    sessionID返却
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SessionManager.ts | `src/node/db/SessionManager.ts` | ソース | セッション管理本体 |
| DB.ts | `src/node/db/DB.ts` | ソース | DB抽象化 |
| GroupManager.ts | `src/node/db/GroupManager.ts` | ソース | グループ確認 |
| AuthorManager.ts | `src/node/db/AuthorManager.ts` | ソース | 著者確認 |
| SecurityManager.ts | `src/node/db/SecurityManager.ts` | ソース | アクセス制御 |
| API.ts | `src/node/db/API.ts` | ソース | API層 |
