# 画面設計書 330-アプリケーション一覧

## 概要

本ドキュメントは、GitLabの「OAuthアプリケーション一覧（Applications）」画面の設計仕様を定義するものです。

### 本画面の処理概要

本画面は、ユーザーが作成したOAuthアプリケーションの管理、および外部アプリケーションからの認可状況を確認・管理するための設定画面です。GitLabをOAuthプロバイダーとして使用するアプリケーションの登録・編集・削除、およびユーザーが認可したアプリケーションの確認・取り消しができます。

**業務上の目的・背景**：OAuth 2.0は、サードパーティアプリケーションがユーザーの資格情報を直接取得せずにユーザーのリソースにアクセスするための標準的な認可プロトコルです。GitLabでは、ユーザーが自身のアプリケーションをOAuthクライアントとして登録し、GitLabの認証・認可機能を利用できます。また、ユーザーが外部アプリケーションに与えた認可を確認し、必要に応じて取り消すことができます。

**画面へのアクセス方法**：
- ユーザー設定 > Applications（`/-/user_settings/applications`）
- サイドバーメニューからのアクセス

**主要な操作・処理内容**：
1. 自身が作成したOAuthアプリケーションの一覧表示
2. 新しいOAuthアプリケーションの登録
3. 既存アプリケーションの編集・削除
4. 認可済みアプリケーションの一覧表示
5. 認可の取り消し

**画面遷移**：
- 遷移元：ユーザー設定画面
- 遷移先：アプリケーション詳細画面、アプリケーション編集画面

**権限による表示制御**：
- ログイン済みユーザーのみアクセス可能
- GitLab設定でuser_oauth_applicationsが無効な場合、新規アプリケーション作成は不可（警告メッセージを表示）

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 140 | 外部認証連携 | 主機能 | OAuthアプリケーション管理 |

## 画面種別

一覧/登録

## URL/ルーティング

- URL: `/-/user_settings/applications`
- コントローラ: `Oauth::ApplicationsController#index`
- レイアウト: `profile`

## 入出力項目

| 項目名 | 項目ID | 入出力 | 型 | 必須 | 説明 |
|--------|--------|--------|-----|------|------|
| アプリケーション名 | name | 入力 | String | ○ | アプリケーションの表示名 |
| リダイレクトURI | redirect_uri | 入力 | Text | ○ | OAuth認可後のコールバックURL（複数行で複数指定可） |
| Confidential | confidential | 入力 | Boolean | - | 機密アプリケーションかどうか |
| スコープ | scopes | 入力 | Array | ○ | アプリケーションがアクセス可能な権限範囲 |

## 表示項目

| 項目名 | 説明 | データソース |
|--------|------|-------------|
| ページタイトル | 「Applications」 | 固定文言（i18n対応） |
| セクション説明 | OAuthアプリケーション管理の説明文 | 動的（oauth_applications_enabledフラグに依存） |
| Your applications（作成済みアプリ一覧） | ユーザーが作成したアプリケーション | current_user.oauth_applications |
| アプリ名 | アプリケーション名（詳細画面へのリンク） | application.name |
| Callback URL | リダイレクトURI | application.redirect_uri |
| Clients | アクティブなアクセストークン数 | application.access_tokens.count |
| Authorized applications（認可済みアプリ一覧） | ユーザーが認可したアプリケーション | current_user.oauth_authorized_tokens |
| 認可日時 | トークンの作成日時 | token.created_at |
| スコープ | 認可されたスコープ | token.scopes |

## イベント仕様

### 1-Add new applicationボタン押下

- **トリガー**: 「Add new application」ボタンのクリック
- **処理内容**: アプリケーション登録フォームの表示をトグル
- **遷移先**: 同画面（フォーム展開）

### 2-Save applicationボタン押下

- **トリガー**: フォームの「Save application」ボタンのクリック
- **処理内容**:
  1. フォームデータをOauth::ApplicationsController#createに送信
  2. Applications::CreateServiceでアプリケーションを作成
  3. 成功時はアプリケーション詳細画面（showアクション）を表示
  4. 失敗時は同画面を再表示してエラーを表示
- **遷移先**:
  - 成功時: アプリケーション詳細画面
  - 失敗時: 同画面（エラーメッセージ付き）
- **HTTPメソッド**: POST

### 3-Editボタン押下（Your applications）

- **トリガー**: 一覧の編集アイコンボタンのクリック
- **処理内容**: アプリケーション編集画面へ遷移
- **遷移先**: `/oauth/applications/:id/edit`

### 4-Destroyボタン押下（Your applications）

- **トリガー**: 一覧の削除アイコンボタンのクリック
- **処理内容**:
  1. 確認ダイアログを表示（"Are you sure?"）
  2. 確認後、アプリケーションを削除
- **遷移先**: 同画面（リスト更新）
- **HTTPメソッド**: DELETE

### 5-Revokeボタン押下（Authorized applications）

- **トリガー**: 認可済みアプリ一覧の「Revoke」ボタンのクリック
- **処理内容**:
  1. 確認ダイアログを表示（"Are you sure?"）
  2. 確認後、認可を取り消し（トークンを削除）
- **遷移先**: 同画面（リスト更新）
- **HTTPメソッド**: DELETE

### 6-Cancelボタン押下

- **トリガー**: フォームの「Cancel」ボタンのクリック
- **処理内容**: 入力フォームを閉じる
- **遷移先**: 同画面（フォーム非表示）

### 7-ページネーション

- **トリガー**: ページネーションリンクのクリック
- **処理内容**: 指定されたカーソル位置のアプリケーション一覧を取得
- **遷移先**: 同画面（異なるページ）
- **HTTPメソッド**: GET

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| Save application押下 | oauth_applications | INSERT | OAuthアプリケーションの作成 |
| Destroy押下（Your applications） | oauth_applications | DELETE | OAuthアプリケーションの削除 |
| Revoke押下（Authorized applications） | oauth_access_tokens | DELETE | 認可トークンの削除 |

### テーブル別更新項目詳細

#### oauth_applications (INSERT)

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | フォームから | アプリケーション名 |
| INSERT | redirect_uri | フォームから | リダイレクトURI |
| INSERT | confidential | フォームから | 機密フラグ |
| INSERT | scopes | フォームから | スコープ |
| INSERT | owner_id | current_user.id | 所有者のユーザーID |
| INSERT | owner_type | 'User' | 所有者タイプ |
| INSERT | organization_id | Current.organization | 組織ID |
| INSERT | uid | 自動生成 | クライアントID |
| INSERT | secret | 自動生成 | クライアントシークレット |

#### oauth_applications (DELETE)

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | id = params[:id] AND owner = current_user | 所有者のアプリケーションのみ削除可能 |

#### oauth_access_tokens (DELETE)

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | application_id = params[:application_id] AND resource_owner_id = current_user.id | 認可トークンの削除 |

## メッセージ仕様

| メッセージID | メッセージ内容 | 表示条件 | 種別 |
|-------------|--------------|---------|------|
| create_success | Application was successfully created. | アプリケーション作成成功時 | 成功 |
| destroy_success | Application was successfully destroyed. | アプリケーション削除成功時 | 成功 |
| scopes_blank | Scopes can't be blank | スコープが未選択の場合 | バリデーションエラー |
| oauth_disabled | Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission. | user_oauth_applicationsが無効な場合 | 警告 |
| no_applications | You don't have any applications. | 作成済みアプリがない場合 | 情報 |
| no_authorized | You don't have any authorized applications. | 認可済みアプリがない場合 | 情報 |
| confirm_destroy | Are you sure? | 削除確認時 | 確認 |

## 例外処理

| 例外状況 | 対応 |
|---------|------|
| アプリケーションが見つからない | Not Found画面を表示（404） |
| バリデーションエラー | フォームを再表示してエラーを表示 |
| 他ユーザーのアプリケーションへのアクセス | Not Found画面を表示（set_applicationでcurrent_userスコープ） |

## 備考

- 国際化（i18n）対応済み
- profileレイアウトを使用（ユーザー設定画面のサイドバー付き）
- アプリケーション一覧はkeyset paginationを使用（cursor based）
- 認可済みアプリケーションは最新トークンのみ表示（latest_per_application）
- Confidentialアプリケーションはバックエンドサーバーでの使用を想定（シークレットを安全に保管できる環境）
- 削除確認にはdata-confirm属性によるJavaScript確認ダイアログを使用
- フォームはCrudComponentによるトグル表示

---

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

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

### 推奨読解順序

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

OAuthアプリケーションの構造を理解するために、モデルを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | oauth_application.rb | `app/models/authn/oauth_application.rb` | Doorkeeper::Applicationを継承、organizationとの関連 |

**読解のコツ**:
- Doorkeeper gemのApplicationモデルを継承している点に注意
- secret_matches?メソッドでフォールバック戦略を確認

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

コントローラの処理フローを確認します。

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

**主要処理フロー**:
1. **14行目**: `verify_user_oauth_applications_enabled` - OAuthアプリ機能の有効/無効チェック
2. **24-26行目**: `index`アクション - set_index_varsを呼び出してデータ取得
3. **30-42行目**: `create`アクション - CreateServiceでアプリケーション作成
4. **64-73行目**: `set_index_vars` - 一覧表示に必要な変数を設定

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

アプリケーション作成のビジネスロジックを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | create_service.rb | `app/services/applications/create_service.rb` | executeメソッドでアプリ作成 |

**主要処理フロー**:
- **24-36行目**: `execute`メソッド - スコープのバリデーションとアプリケーション保存

#### Step 4: ビューテンプレートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.html.haml | `app/views/doorkeeper/applications/index.html.haml` | メインテンプレート、shared partialを呼び出し |
| 4-2 | _index.html.haml | `app/views/shared/doorkeeper/applications/_index.html.haml` | Your applicationsとAuthorized applicationsセクション |
| 4-3 | _form.html.haml | `app/views/shared/doorkeeper/applications/_form.html.haml` | アプリケーション登録フォーム |
| 4-4 | _delete_form.html.haml | `app/views/shared/doorkeeper/applications/_delete_form.html.haml` | 削除フォーム |

**主要処理フロー**:
- **1-8行目** (index.html.haml): ページタイトル設定、sharedパーシャルを呼び出し
- **16-56行目** (_index.html.haml): Your applications（CrudComponent）の表示
- **62-98行目** (_index.html.haml): Authorized applications（CrudComponent）の表示
- **1-30行目** (_form.html.haml): 名前、リダイレクトURI、Confidential、スコープの入力フィールド

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

```
Oauth::ApplicationsController
    │
    ├─ index (GET /-/user_settings/applications)
    │      ├─ authenticate_user! (before_action)
    │      ├─ verify_user_oauth_applications_enabled (before_action)
    │      ├─ load_scopes (before_action)
    │      ├─ set_index_vars
    │      │      ├─ current_user.oauth_applications.keyset_paginate
    │      │      ├─ current_user.oauth_authorized_tokens
    │      │      └─ Authn::OauthApplication.new
    │      └─ render :index
    │             └─ doorkeeper/applications/index.html.haml
    │                    └─ shared/doorkeeper/applications/_index.html.haml
    │                           ├─ shared/doorkeeper/applications/_form.html.haml
    │                           └─ shared/doorkeeper/applications/_delete_form.html.haml
    │
    └─ create (POST /-/user_settings/applications)
           ├─ Applications::CreateService.new.execute
           │      └─ Authn::OauthApplication.new + save
           │
           ├─ 成功時
           │      └─ render :show
           │
           └─ 失敗時
                  ├─ set_index_vars
                  └─ render :index
```

### データフロー図

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

画面アクセス ───▶ index ───▶ アプリケーション一覧表示
                   │
                   ├─▶ @applications (自分のアプリ)
                   │
                   ├─▶ @authorized_tokens (認可済みアプリ)
                   │
                   └─▶ @application (新規フォーム用)

フォーム送信 ───▶ create ───▶ アプリ詳細画面 / エラー表示
  │                  │
  │                  └─▶ Applications::CreateService.execute
  │                           │
  │                           └─▶ oauth_applications INSERT
  │
  └── name, redirect_uri, confidential, scopes

削除操作 ───▶ destroy ───▶ 一覧画面
  │                │
  │                └─▶ oauth_applications DELETE
  │
  └── application_id

認可取消 ───▶ doorkeeper/authorized_applications#destroy ───▶ 一覧画面
  │                │
  │                └─▶ oauth_access_tokens DELETE
  │
  └── application_id or token_id
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.html.haml | `app/views/doorkeeper/applications/index.html.haml` | テンプレート | メインビュー |
| _index.html.haml | `app/views/shared/doorkeeper/applications/_index.html.haml` | テンプレート | アプリケーション一覧パーシャル |
| _form.html.haml | `app/views/shared/doorkeeper/applications/_form.html.haml` | テンプレート | 登録フォームパーシャル |
| _delete_form.html.haml | `app/views/shared/doorkeeper/applications/_delete_form.html.haml` | テンプレート | 削除フォームパーシャル |
| applications_controller.rb | `app/controllers/oauth/applications_controller.rb` | ソース | コントローラ |
| oauth_application.rb | `app/models/authn/oauth_application.rb` | ソース | モデル |
| create_service.rb | `app/services/applications/create_service.rb` | ソース | 作成サービス |
