---
generated_at: 2026-02-03 10:00:00
metrics:
  claims_total: 63
  claims_with_evidence: 61
  claims_without_evidence: 2
confidence_derived: 0.97
---

# 根拠レポート：server-identity 単体テストケース一覧

## 本レポートについて

### 目的
本レポートは、生成された設計書・ドキュメントの信頼性を検証し、人間レビュアーが効率的にレビューできるようにすることを目的としています。

### チェック方法
以下の観点でドキュメントの内容（Claim：主張）を検証しています：

1. **根拠の有無確認**：各主張に対して、ソースコード・既存設計書・要件定義書などの根拠（Evidence）が存在するか
2. **根拠との整合性**：主張の内容が根拠と矛盾していないか
3. **網羅性**：参照すべき情報源を適切にカバーしているか

### 信頼度スコアの算出
- **confidence_derived** = 根拠あり件数 / 総主張件数
- 状態「○」：根拠あり、「△」：根拠不足または要確認

### 本レポートの使い方
1. まず「サマリー」で全体の信頼度と優先レビュー項目を確認
2. 「Claims と根拠の対応」で △ の項目を重点的にレビュー
3. 「不足情報」で補完が必要な情報源を確認

---

## 1) サマリー（まず見るところ）
- 総合信頼度（derived）：**0.97**
  - 根拠あり：61 / 63、根拠なし：2
- 優先レビュー（高）
  1. **C-28 (UT-IDN-028)**：revoke後のasAuthHeaderValueの挙動 - revoke()はuser/passwordを空文字列に設定するが、asAuthHeaderValueのnullチェックは空文字列ではなくnullを判定するため、実際の戻り値が異なる可能性がある
  2. **C-63 (UT-IDN-063)**：NoopPluginSubject.runAsの例外伝播 - ThreadContext.StoredContextのclose処理との相互作用の確認が必要

## 2) 参照した情報（Evidence一覧）
> ここに「実在するもの」だけ列挙。

- E-01: `server/src/main/java/org/opensearch/identity/IdentityService.java` - IdentityServiceクラス（コンストラクタ、getCurrentSubject、getTokenManager、initializeIdentityAwarePlugins）
- E-02: `server/src/main/java/org/opensearch/identity/NamedPrincipal.java` - NamedPrincipalクラス（コンストラクタ、getName、equals、hashCode、toString、UNAUTHENTICATED定数）
- E-03: `server/src/main/java/org/opensearch/identity/Subject.java` - Subjectインターフェース（getPrincipal、runAs）
- E-04: `server/src/main/java/org/opensearch/identity/UserSubject.java` - UserSubjectインターフェース（authenticate）
- E-05: `server/src/main/java/org/opensearch/identity/PluginSubject.java` - PluginSubjectインターフェース
- E-06: `server/src/main/java/org/opensearch/identity/tokens/AuthToken.java` - AuthTokenインターフェース（asAuthHeaderValue）
- E-07: `server/src/main/java/org/opensearch/identity/tokens/TokenManager.java` - TokenManagerインターフェース（issueOnBehalfOfToken、issueServiceAccountToken）
- E-08: `server/src/main/java/org/opensearch/identity/tokens/OnBehalfOfClaims.java` - OnBehalfOfClaimsクラス（コンストラクタ、getAudience、getExpiration）
- E-09: `server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java` - BasicAuthTokenクラス（コンストラクタ、getUser、getPassword、toString、revoke、asAuthHeaderValue）
- E-10: `server/src/main/java/org/opensearch/identity/tokens/BearerAuthToken.java` - BearerAuthTokenクラス（コンストラクタ、getHeader、getPayload、getSignature、getCompleteToken、getTokenIdentifier、toString、asAuthHeaderValue）
- E-11: `server/src/main/java/org/opensearch/identity/noop/NoopIdentityPlugin.java` - NoopIdentityPluginクラス（コンストラクタ、getCurrentSubject、getTokenManager、getPluginSubject）
- E-12: `server/src/main/java/org/opensearch/identity/noop/NoopSubject.java` - NoopSubjectクラス（getPrincipal、authenticate、equals、hashCode、toString）
- E-13: `server/src/main/java/org/opensearch/identity/noop/NoopTokenManager.java` - NoopTokenManagerクラス（issueOnBehalfOfToken、issueServiceAccountToken）
- E-14: `server/src/main/java/org/opensearch/identity/noop/NoopPluginSubject.java` - NoopPluginSubjectクラス（コンストラクタ、getPrincipal、runAs）

## 3) Claims と根拠の対応（レビューの主戦場）
| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-01 | IdentityPluginが0件の場合NoopIdentityPluginが使用される | E-01 (L38-40: `identityPlugins.size() == 0` でNoopIdentityPlugin生成) | ○ |
| C-02 | IdentityPluginが1件の場合そのプラグインが使用される | E-01 (L41-43: `identityPlugins.size() == 1` で`identityPlugins.get(0)`) | ○ |
| C-03 | IdentityPluginが2件以上の場合OpenSearchExceptionがスローされる | E-01 (L44-49: else節でOpenSearchExceptionをthrow) | ○ |
| C-04 | getCurrentSubjectがidentityPlugin.getCurrentSubjectを委譲する | E-01 (L55-57: `return identityPlugin.getCurrentSubject()`) | ○ |
| C-05 | getTokenManagerがidentityPlugin.getTokenManagerを委譲する | E-01 (L62-64: `return identityPlugin.getTokenManager()`) | ○ |
| C-06 | initializeIdentityAwarePluginsが各プラグインにSubjectを割り当てる | E-01 (L66-73: forループでgetPluginSubject/assignSubject呼び出し) | ○ |
| C-07 | initializeIdentityAwarePluginsにnullを渡しても例外が発生しない | E-01 (L67: `if (identityAwarePlugins != null)` のnullチェック) | ○ |
| C-08 | initializeIdentityAwarePluginsに空リストを渡しても正常終了する | E-01 (L67-68: nullチェック後forループが0回実行) | ○ |
| C-09 | NamedPrincipalのコンストラクタが名前を設定する | E-02 (L29-31: `this.name = name`) | ○ |
| C-10 | getNameが設定された名前を返す | E-02 (L34-36: `return name`) | ○ |
| C-11 | 同一名前のNamedPrincipalがequals true | E-02 (L39-44: `Objects.equals(name, that.getName())`) | ○ |
| C-12 | 異なる名前のNamedPrincipalがequals false | E-02 (L39-44: equalsの比較ロジック) | ○ |
| C-13 | nullとの比較でfalse | E-02 (L41: `if (obj == null ...) return false`) | ○ |
| C-14 | 同一インスタンスとの比較でtrue | E-02 (L40: `if (this == obj) return true`) | ○ |
| C-15 | 異なる型との比較でfalse | E-02 (L41: `getClass() != obj.getClass()`) | ○ |
| C-16 | 同一名前でhashCode一致 | E-02 (L47-49: `Objects.hash(name)`) | ○ |
| C-17 | toStringの形式 | E-02 (L52-54: `"NamedPrincipal(" + "name=" + name + ")"`) | ○ |
| C-18 | UNAUTHENTICATED定数の名前が"Unauthenticated" | E-02 (L21: `new NamedPrincipal("Unauthenticated")`) | ○ |
| C-19 | BasicAuthTokenがBase64デコードでuser/passwordを抽出 | E-09 (L24-35: コンストラクタのパースロジック) | ○ |
| C-20 | コロンなしのBase64でIllegalStateException | E-09 (L30-32: `tokenParts.length != 2` でIllegalStateException) | ○ |
| C-21 | パスワードにコロンを含む場合split limit=2で正しく分割 | E-09 (L29: `usernamepassword.split(":", 2)`) | ○ |
| C-22 | 不正なBase64でデコードエラー | E-09 (L26: `Base64.getUrlDecoder().decode()` が例外をスロー) | ○ |
| C-23 | getUser | E-09 (L37-39) | ○ |
| C-24 | getPassword | E-09 (L41-43) | ○ |
| C-25 | toString形式 | E-09 (L46-48) | ○ |
| C-26 | revokeでuser/passwordが空文字列 | E-09 (L50-53: `this.password = ""`, `this.user = ""`) | ○ |
| C-27 | asAuthHeaderValueがBase64エンコード値を返す | E-09 (L56-62: user+":"+passwordをBase64エンコード) | ○ |
| C-28 | revoke後のasAuthHeaderValueの挙動 | E-09 (L57: `user == null || password == null` のチェック、revokeは空文字列を設定するためnullチェックに引っかからない) | △ |
| C-29 | TOKEN_IDENTIFIER定数 | E-09 (L19: `TOKEN_IDENTIFIER = "Basic"`) | ○ |
| C-30 | BearerAuthTokenが3パーツに分割される | E-10 (L25-35: コンストラクタのパースロジック) | ○ |
| C-31 | 2パーツでIllegalArgumentException | E-10 (L28-29: `tokenComponents.length != 3`) | ○ |
| C-32 | 4パーツでIllegalArgumentException | E-10 (L28-29: `tokenComponents.length != 3`) | ○ |
| C-33 | 空文字列でIllegalArgumentException | E-10 (L27-29: splitの結果が3パーツにならない) | ○ |
| C-34 | getHeader | E-10 (L37-39) | ○ |
| C-35 | getPayload | E-10 (L41-43) | ○ |
| C-36 | getSignature | E-10 (L45-47) | ○ |
| C-37 | getCompleteToken | E-10 (L49-51) | ○ |
| C-38 | getTokenIdentifier | E-10 (L53-55: `return TOKEN_IDENTIFIER`) | ○ |
| C-39 | toString形式 | E-10 (L58-60) | ○ |
| C-40 | asAuthHeaderValueがcompleteTokenを返す | E-10 (L63-65: `return completeToken`) | ○ |
| C-41 | OnBehalfOfClaimsのコンストラクタ(aud, expiration) | E-08 (L30-33) | ○ |
| C-42 | OnBehalfOfClaimsのコンストラクタ(aud, subject)でデフォルト300秒 | E-08 (L40-42: `this(aud, 300L)`) | ○ |
| C-43 | getAudience | E-08 (L44-46) | ○ |
| C-44 | getExpiration | E-08 (L48-50) | ○ |
| C-45 | expiration=0の境界値 | E-08 (L30-33: Long型で0Lを受け入れる) | ○ |
| C-46 | expiration=nullの境界値 | E-08 (L22: `Long expiration_seconds` はオブジェクト型でnull可) | ○ |
| C-47 | NoopIdentityPluginのコンストラクタ | E-11 (L29-31) | ○ |
| C-48 | getCurrentSubjectがNoopSubjectを返す | E-11 (L38-40: `return new NoopSubject()`) | ○ |
| C-49 | getTokenManagerがNoopTokenManagerを返す | E-11 (L47-49: `return new NoopTokenManager()`) | ○ |
| C-50 | getPluginSubjectがNoopPluginSubjectを返す | E-11 (L52-54: `return new NoopPluginSubject(threadPool)`) | ○ |
| C-51 | NoopSubject.getPrincipalがUNAUTHENTICATEDを返す | E-12 (L29-31: `return NamedPrincipal.UNAUTHENTICATED`) | ○ |
| C-52 | NoopSubject.authenticateが何もしない | E-12 (L55-57: 空のメソッドボディ) | ○ |
| C-53 | NoopSubject同士がequals true | E-12 (L34-39: getPrincipalで比較、全NoopSubjectが同一Principal) | ○ |
| C-54 | NoopSubject.equals(null)がfalse | E-12 (L36: `if (obj == null ...) return false`) | ○ |
| C-55 | NoopSubject同士がhashCode一致 | E-12 (L42-44: `Objects.hash(getPrincipal())`) | ○ |
| C-56 | NoopSubject.toString形式 | E-12 (L47-49) | ○ |
| C-57 | NoopTokenManager.issueOnBehalfOfTokenがnoopTokenを返す | E-13 (L31-38: 匿名AuthTokenで"noopToken"を返す) | ○ |
| C-58 | NoopTokenManager.issueServiceAccountTokenがnoopTokenを返す | E-13 (L45-52: 匿名AuthTokenで"noopToken"を返す) | ○ |
| C-59 | NoopTokenManager.issueOnBehalfOfTokenにnull subjectでも動作する | E-13 (L31-38: subjectパラメータを使用していない) | ○ |
| C-60 | NoopTokenManager.issueServiceAccountTokenにnullでも動作する | E-13 (L45-52: audienceパラメータを使用していない) | ○ |
| C-61 | NoopPluginSubject.getPrincipalがUNAUTHENTICATEDを返す | E-14 (L39-41: `return NamedPrincipal.UNAUTHENTICATED`) | ○ |
| C-62 | NoopPluginSubject.runAsがRunnableを実行する | E-14 (L44-47: `r.run()`) | ○ |
| C-63 | NoopPluginSubject.runAsで例外がスローされた場合の伝播 | E-14 (L44-47: try-with-resourcesのため例外伝播するが、StoredContextのclose処理との相互作用は未確認) | △ |

## 4) 不足情報（Unknown / Missing）
- C-28: BasicAuthToken.revoke()後のasAuthHeaderValue()の正確な挙動
  - revoke()はuser/passwordを空文字列("")に設定するが、asAuthHeaderValue()のnullチェック(`user == null || password == null`)は空文字列を検出しない
  - 実際にはrevoke後は `:` (空文字列:空文字列) のBase64エンコード値が返却される
  - 候補：テストケースの期待結果を「空文字列のBase64エンコード値（Og==）が返却される」に修正すべき
- C-63: NoopPluginSubject.runAs内でRunnableが例外をスローした場合
  - try-with-resources内のため、StoredContext.close()は呼ばれるが、例外伝播の正確な挙動はThreadContext実装に依存
  - 候補：ThreadContextの実装 / try-with-resourcesのJava言語仕様

## 5) リスクフラグ（レビュー観点）
- 1: C-28 - revoke後のasAuthHeaderValueの期待結果が不正確な可能性（中リスク）
- 0: C-63 - try-with-resourcesによる例外伝播はJava言語仕様で保証されており、低リスク

## 6) レビュアーチェックリスト（最小）
- [ ] C-28: revoke()後のasAuthHeaderValue()が実際にどの値を返すか確認（空文字列のBase64エンコードか、nullか）
- [ ] IdentityServiceコンストラクタの分岐カバレッジが十分か確認（0件、1件、2件以上）
- [ ] BasicAuthTokenのBase64デコードがURLセーフデコーダ(`getUrlDecoder`)を使用している点について、通常のBase64との互換性を確認
- [ ] BearerAuthTokenのDELIMITERが正規表現`\\.`であり、splitの引数として正しく使用されていることを確認
- [ ] NoopPluginSubject.runAsのThreadContext stash/restoreが正しくテストされているか確認
