Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 11 additions & 17 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ https://github.com/OpenTouryoProject/OpenTouryo/

と、ソレを使用したサンプル・アプリケーションの開発エージェント用。

環境のセットアップとビルド手順は [root/Readme.ja.md](root/Readme.ja.md) を参照。
環境のセットアップ・ビルド・検証の手順は [root/Readme.ja.md](root/Readme.ja.md) を参照。
本書は、その上で**エージェントが守るべきこと**と、**どの文書を見るか**を示す。

**プロジェクト共通の投稿規約は [Contributing.ja.md](Contributing.ja.md) に従う。**
Expand Down Expand Up @@ -170,23 +170,17 @@ Copyright ブロックの扱い、`ArgumentException` 系の引数の順、`.bat

### ビルド・テスト・リリースの検証は、専用の文書に従う

変更を加えたあとの**検証**は、次の文書が一次情報である。
**検証の手順そのものは、人もエージェントも同じである。**
入口は [root/Readme.ja.md](root/Readme.ja.md) の「ビルド後の検証」で、
3 本のスクリプト(ビルド → 単体テスト → 疎通)と、その一次情報
(`BUILDING.md` / `TESTING.md` / `SMOKETEST.md`、リリース時の作業全体は `RELEASE.md`)は、
そこから辿れる。**本節は、その上で残るエージェント固有の判断だけを扱う。**

| 目的 | 読む文書 |
|---|---|
| **手順だけを引く(早見)** | [`CHEATSHEET.md`](root/programs/CHEATSHEET.md) |
| リリース時の作業全体 | [`RELEASE.md`](root/programs/RELEASE.md) |
| 全ビルドの実行と判定 | [`BUILDING.md`](root/programs/BUILDING.md) |
| 単体テストの実行と判定 | [`TESTING.md`](root/programs/TESTING.md) |
| サンプルの疎通確認 | [`SMOKETEST.md`](root/programs/SMOKETEST.md) |

検証は `1_BuildAll.ps1`(ビルド)→ `2_RunAllTests.ps1`(単体テスト)→
`3_SmokeTest.ps1`(疎通)の 3 本で、いずれも終了コードで合否が分かる。
**順序は固定**(1 のクリーンとアセンブリ配置が 2・3 の前提)。

**本書にコマンドは書かない。** 引数(`-IgnoreErrors` / `-Only` / `-List` / `-Lang`)と
合格の目安は [`CHEATSHEET.md`](root/programs/CHEATSHEET.md) 1 節が一次情報である。
**転記すると、両方を直さないかぎりズレる。**
**手順だけを引くなら [`CHEATSHEET.md`](root/programs/CHEATSHEET.md) 1 節。**
引数(`-IgnoreErrors` / `-Only` / `-List` / `-Lang`)・合格の目安・
実行順(**固定**。1 のクリーンとアセンブリ配置が 2・3 の前提)は、そこが一次情報である。

**本書にコマンドは書かない。転記すると、両方を直さないかぎりズレる。**

`2_RunAllTests.ps1` はワーキング ツリーの `Result*.txt` を書き換える(従来のバッチ運用と同じ)。
**コミットの要否は人が判断する**ため、エージェントは差分を報告するに留める。
Expand Down
3 changes: 3 additions & 0 deletions root/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ cd C:\root\programs
| `2_RunAllTests.ps1` | Runs the unit tests and compares the results with the previous ones |
| `3_SmokeTest.ps1` | Starts the sample applications and checks that they work |

The three can also be run individually. **In that case the order is fixed**,
because the clean and the assembly deployment done by `1_` are prerequisites of `2_` and `3_`.

For the procedures and the criteria, refer to
[`BUILDING.md`](programs/BUILDING.md) / [`TESTING.md`](programs/TESTING.md) /
[`SMOKETEST.md`](programs/SMOKETEST.md) in the same folder.
Expand Down
3 changes: 3 additions & 0 deletions root/Readme.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ cd C:\root\programs
| `2_RunAllTests.ps1` | 単体テストを実行し、結果を前回のものと比較 |
| `3_SmokeTest.ps1` | サンプルアプリケーションを起動して疎通を確認 |

3 本は個別に実行することもできます。**その場合の実行順は固定**です
(`1_` のクリーンとアセンブリ配置が `2_` `3_` の前提になっているため)。

手順と判定基準は、同じフォルダの
[`BUILDING.md`](programs/BUILDING.md) / [`TESTING.md`](programs/TESTING.md) /
[`SMOKETEST.md`](programs/SMOKETEST.md) を参照してください。
Expand Down
61 changes: 61 additions & 0 deletions root/programs/CS/Frameworks/ANALYSIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,67 @@ private void UOC_SelectCount(TestParameterValue p) // private でよい

---

### 4.4 **非同期の実体(`~Async` は「別スレッドで同期実行」)**(#402)

**D層に非同期の API は無い。** `BaseDam` は `ExecuteReader` / `ExecuteScalar` /
`ExecuteNonQuery` の同期版だけを持つ。

上位の `~Async` も、**別スレッドで同期版を走らせているだけ**である。

```csharp
// CallController.cs / BaseLogic.cs
return Task<...>.Factory.StartNew(() => {
return this.Invoke(...); // 同期版
});
```

**これはブロックの移動であって、除去ではない。**
呼び出し元のスレッドは解放されるが、**プールのスレッドが 1 本、DB 応答の間ずっと塞がる。**

#### **非同期化しない、と判断している**(#402)

業務系の DBMS アプリケーションでは、**非同期のサポートは重要ではない**と判断した。

**1 要求が 1 コネクションを保持する設計だからである。**

```
UOC_ConnectionOpen で Dam を作り、コネクションとトランザクションを開く
→ B層の処理がすべて終わるまで保持 → Commit / Rollback
```

**同時実行数は、スレッド数ではなくコネクション プールの上限で決まる。**
スレッドを解放できても、プールが空かなければ次には進めない。
非同期化で増えるのは「**待てる要求の数**」であって、
「**同時に処理できる要求の数**」ではない。

| 論点 | 既存の機構 |
|---|---|
| 同時実行数の制御 | コネクション プール(上限) |
| 応答しない処理の打ち切り | `FxSqlCommandTimeout`(既定 30 秒) |

**非同期化でしか得られないのは「実行中の中断」だけ**だが、
業務システムでは利用者が処理を中断する場面が限られ、多くは `CommandTimeout` で足りる。

#### **プロバイダによって非同期の実体が違う**

**足すとしても、一様には効かない。**
`DbCommand` の `~Async` は、プロバイダが上書きしなければ
**基底クラスの既定実装=呼び出し元スレッドで同期実行**になる。
**`await` してもそこで止まる**ため、今より悪くなり得る。

| Dam | ライブラリ | 非同期の実体 |
|---|---|---|
| `DamSqlSvr` | `Microsoft.Data.SqlClient` | 真の非同期 |
| `DamPstGrS` | `Npgsql` | 真の非同期 |
| `DamMySQL` | `MySql.Data` | **要検証** |
| `DamManagedOdp` | `Oracle.ManagedDataAccess` | **要検証** |
| `DamODBC` / `DamOLEDB` | 標準 | **効果なし**(既定実装) |

> **`~Async` という名前は「非同期である」と読まれる。**
> 効かないプロバイダに足すと、名前だけが非同期になり、実態を隠す。

---

## 5. 設定(XML 定義ファイル + appSettings)

### 5.1 XML 定義ファイル(`root/files/resource/Xml/`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
//* ---------- ---------------- -------------------------------------------------
//* 2018/08/22 西野 大介 新規作成
//* 2018/11/28 西野 大介 証明書 & Jwk対応 + jkuチェック対応の追加
//* 2026/09/10 玄人 幸道 CreateHashが左半分でなく左右のXORを返していたのを修正(#584)
//**********************************************************************************

using System;
Expand Down Expand Up @@ -209,6 +210,19 @@ public static bool Verify(string id_token,
/// </summary>
/// <param name="input">string</param>
/// <returns>hash</returns>
/// <remarks>
/// OIDC の定義(OpenID Connect Core 1.0 3.1.3.6 / 3.3.2.11)どおり、
/// ハッシュの**左半分**(先頭 bytes.Length / 2 バイト)を base64url にする。
///
/// 2026/09/10 まで ArrayOperator.ShortenByteArray で半分にしていたため、
/// 左半分ではなく**左半分と右半分の XOR** になっていた(#584)。
/// ShortenByteArray は切り詰めではなく XOR による畳み込みで、
/// 鍵長の調整(GetKeyedHash)がその挙動に依存するため、あちらは直さない。
///
/// VerifyHash も同じ CreateHash で作り直して比べるため、
/// 本フレームワーク同士では誤っていても一致し、気付けなかった。
/// **修正前の版で発行された値は、修正後の VerifyHash では検証に失敗する。**
/// </remarks>
public static string CreateHash(string input)
{
// ID Token の JOSE Header にある
Expand All @@ -219,8 +233,9 @@ public static string CreateHash(string input)
EnumHashAlgorithm.SHA256);

// 左半分を base64url エンコードした値。
// (ShortenByteArray は XOR で畳み込むため使わない。#584)
return CustomEncode.ToBase64UrlString(
ArrayOperator.ShortenByteArray(bytes, (bytes.Length / 2)));
ArrayOperator.CopyArray<byte>(bytes, (bytes.Length / 2)));
}

/// <summary>
Expand Down
21 changes: 0 additions & 21 deletions root/programs/CS/Frameworks/Readme.txt

This file was deleted.

5 changes: 5 additions & 0 deletions root/programs/CS/Frameworks/Tests/TestCode/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
//* 2026/08/18 玄人 幸道 各テストを個別に try で囲むようにした(#564)。
//* 1 つが例外を投げると、以降が実行されなかった。
//* 2026/08/08 玄人 幸道 TestZipV2の呼び出しを追加(#524)
//* 2026/09/10 玄人 幸道 TestIdTokenの呼び出しを追加(#584)
//**********************************************************************************

using System;
Expand Down Expand Up @@ -107,6 +108,10 @@ public static void Main(string[] args)
// DB に接続するテストと前提が異なるため、プロジェクトを分けている。
#endregion

#region Framework
Program.Run("TestIdToken", TestIdToken.Root);
#endregion

#region Business
// Touryo.Infrastructure.Business
// GMTMaster
Expand Down
19 changes: 18 additions & 1 deletion root/programs/CS/Frameworks/Tests/TestCode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ y_Build_TestCode_Public.bat … ビルド → 実行 → 結果ファイル
| `TestLatebind` | `Public.Reflection` | 遅延バインドによる呼び出し(`NonPublic` を含む) |
| `TestFastReflection` | `Public.FastReflection` | `AccessorCacher` / `InstanceCreator` 等の高速リフレクション(#522) |

### Framework

| ファイル | 対象 | 見ていること |
|---|---|---|
| `TestIdToken` | `Framework.Authentication` | `at_hash` / `c_hash` / `s_hash` の計算(`IdToken.CreateHash` / `VerifyHash`)。**期待値は OIDC 仕様の例示値**(#584) |

`TestEmbedded.txt` は `TestResourceLoader` が読む埋め込みリソース。
**`LogicalName` を `TestCode.TestEmbedded.txt` に固定している**
(既定のままだと `TestCodeFx` / `TestCodeCore` で名前が食い違うため)。
Expand Down Expand Up @@ -117,6 +123,17 @@ MyDebug.OutputDebugAndConsole(
桁の重みを `256 * j`(掛け算)で求めていたため 3 byte 目から誤っていたが、
8 byte の出力をそのまま期待値にしていたため、いったん見逃した。

### 「作って、同じ実装で確かめる」は検算にならない

**作る側と確かめる側が同じ実装を使うと、誤っていても一致する。**

`IdToken.VerifyHash` は、`CreateHash` で作り直した値と比べるだけである。
`CreateHash` は OIDC の定義(ハッシュの左半分)ではなく左右の XOR を返していたが、
本フレームワーク同士では一致するため、**外部の RP に拒否されるまで気付かれなかった**(#584)。

**期待値には、実装と独立に求めた値を置く。** 仕様の例示値があればそれを使う
(`TestIdToken` は OpenID Connect Core 1.0 付録 A の値を使っている)。

### 境界は必ず跨ぐ

同じ `GetLongFromByte` は、**1〜2 byte だけを見ていると不具合に気付けない。**
Expand Down Expand Up @@ -168,7 +185,7 @@ net48 側は `4_Build_CopyAssemblies.bat` が依存 DLL を `Build_net48` へ並
|---|---|
| `PubCmnFunction.GetFileNameNoEx` | 第 2 引数は**パス区切り**。拡張子の区切りだと思って `'.'` を渡すと空文字が返る |
| `ArrayOperator.CopyArray` | **配列を伸ばせない。** コピー長が「コピー先配列の長さ」で固定のため、コピー先を大きくするとコピー元が足りず落ちる。書込開始位置を 0 より後ろにしても落ちる |
| `ArrayOperator.ShortenByteArray` | 単純な切り詰めではなく、**XOR で畳み込む**(暗号鍵の生成用) |
| `ArrayOperator.ShortenByteArray` | 単純な切り詰めではなく、**XOR で畳み込む**(暗号鍵の生成用)。**切り詰めに使ってはならない**(`IdToken.CreateHash` がこれで誤った、#584)。切り詰めは `CopyArray` |
| `ArrayOperator.GetLongFromByte` | ビッグ エンディアン。8 byte で最上位ビットが立つと **Int64 の範囲を超えて負になる**(2 の補数として解釈した値) |
| `StringExtractor.GetParameterFromQueryString` | 「値が空」と「名前が無い」を**区別できない**(どちらも `""`) |
| `ObjectInspector` | 再帰の深さは **5 まで**。入口で加算・出口で減算するカウンタで抑えている |
Expand Down
19 changes: 14 additions & 5 deletions root/programs/CS/Frameworks/Tests/TestCode/Result48.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level
log4net: Created Appender [OPERATION2]
log4net: Adding appender named [OPERATION2] to logger [OPERATION].
log4net: Hierarchy Threshold []
[2026/08/20 10:54:24,630],[DEBUG],[1],LogIF.DebugLog("ACCESS");
[2026/08/20 10:54:24,639],[INFO ],[1],LogIF.InfoLog("ACCESS");
[2026/08/20 10:54:24,640],[WARN ],[1],LogIF.WarnLog("ACCESS");
[2026/08/20 10:54:24,640],[ERROR],[1],LogIF.ErrorLog("ACCESS");
[2026/08/20 10:54:24,640],[FATAL],[1],LogIF.FatalLog("ACCESS");
[2026/09/10 22:27:52,562],[DEBUG],[1],LogIF.DebugLog("ACCESS");
[2026/09/10 22:27:52,568],[INFO ],[1],LogIF.InfoLog("ACCESS");
[2026/09/10 22:27:52,569],[WARN ],[1],LogIF.WarnLog("ACCESS");
[2026/09/10 22:27:52,569],[ERROR],[1],LogIF.ErrorLog("ACCESS");
[2026/09/10 22:27:52,569],[FATAL],[1],LogIF.FatalLog("ACCESS");
LogIF.Is*Enabled - ACCESS : Debug True / Info True / Warn True / Error True / Fatal True
log4net: configuring repository [log4net-default-repository] using stream
log4net: loading XML configuration
Expand Down Expand Up @@ -2963,4 +2963,13 @@ CompiledExpressionCreater
フィールド Field : フィールドの値
存在しないメンバ : 例外 System.ArgumentException
----------------------------------------------------------------------------------------------------
IdToken.CreateHash
at_hash(OIDC Core 付録 A) : 77QmUPtjPfzWtF2AnpK9RQ(期待値と一致 : True)
c_hash(OIDC Core 付録 A) : LDktKdoQak3Pk0cnXxCltA(期待値と一致 : True)
s_hash(#584) : QCiR2bEHBPw1Pd2r2ZtS_Q(期待値と一致 : True)
----------------------------------------------------------------------------------------------------
IdToken.VerifyHash
左半分(OIDC の定義) : True
左右の XOR(修正前の値) : False
----------------------------------------------------------------------------------------------------
log4net: Shutdown called on Hierarchy [log4net-default-repository]
19 changes: 14 additions & 5 deletions root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level
log4net: Created Appender [OPERATION2]
log4net: Adding appender named [OPERATION2] to logger [OPERATION].
log4net: Hierarchy Threshold []
[2026/08/20 10:54:29,651],[DEBUG],[2],LogIF.DebugLog("ACCESS");
[2026/08/20 10:54:29,660],[INFO ],[2],LogIF.InfoLog("ACCESS");
[2026/08/20 10:54:29,661],[WARN ],[2],LogIF.WarnLog("ACCESS");
[2026/08/20 10:54:29,662],[ERROR],[2],LogIF.ErrorLog("ACCESS");
[2026/08/20 10:54:29,662],[FATAL],[2],LogIF.FatalLog("ACCESS");
[2026/09/10 22:27:55,097],[DEBUG],[2],LogIF.DebugLog("ACCESS");
[2026/09/10 22:27:55,105],[INFO ],[2],LogIF.InfoLog("ACCESS");
[2026/09/10 22:27:55,105],[WARN ],[2],LogIF.WarnLog("ACCESS");
[2026/09/10 22:27:55,106],[ERROR],[2],LogIF.ErrorLog("ACCESS");
[2026/09/10 22:27:55,106],[FATAL],[2],LogIF.FatalLog("ACCESS");
LogIF.Is*Enabled - ACCESS : Debug True / Info True / Warn True / Error True / Fatal True
log4net: configuring repository [log4net-default-repository] using stream
log4net: loading XML configuration
Expand Down Expand Up @@ -2984,4 +2984,13 @@ CompiledExpressionCreater
フィールド Field : フィールドの値
存在しないメンバ : 例外 System.ArgumentException
----------------------------------------------------------------------------------------------------
IdToken.CreateHash
at_hash(OIDC Core 付録 A) : 77QmUPtjPfzWtF2AnpK9RQ(期待値と一致 : True)
c_hash(OIDC Core 付録 A) : LDktKdoQak3Pk0cnXxCltA(期待値と一致 : True)
s_hash(#584) : QCiR2bEHBPw1Pd2r2ZtS_Q(期待値と一致 : True)
----------------------------------------------------------------------------------------------------
IdToken.VerifyHash
左半分(OIDC の定義) : True
左右の XOR(修正前の値) : False
----------------------------------------------------------------------------------------------------
log4net: Shutdown called on Hierarchy [log4net-default-repository]
Loading