From 9a4b300114aed139e180f898f9e59a34c5366aad Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Fri, 23 Oct 2026 07:09:26 +0900 Subject: [PATCH 01/11] =?UTF-8?q?=E6=97=A2=E7=9F=A5=E3=81=AE=E8=84=86?= =?UTF-8?q?=E5=BC=B1=E6=80=A7=E3=81=8C=E3=81=82=E3=82=8B=E3=83=91=E3=83=83?= =?UTF-8?q?=E3=82=B1=E3=83=BC=E3=82=B8=E3=82=92=E4=BF=AE=E6=AD=A3=E7=89=88?= =?UTF-8?q?=E3=81=B8=E4=B8=8A=E3=81=92=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dotnet list package --vulnerable で High が検出されたため更新する。 - Microsoft.AspNetCore.Components.WebAssembly.Server 10.0.10(GHSA-g27p-qf6j-c96c・GHSA-qjpx-jqm5-h2c4) ほか Microsoft.* の 10.0.10 をすべて 10.0.13 に揃える - System.Security.Cryptography.Pkcs 9.0.4(SqlClient 6.1.1 経由。GHSA-846f-f764-76mx ほか)は Infrastructure から 10.0.13 を直接参照して上書きする - AspNetCore.OpenApi 10.0.13 が要求する Microsoft.OpenApi 2.12.2 へ直接参照も上げる(NU1605 の解消) - dotnet-ef ツールを EF Core と同じ 10.0.13 に揃える Co-Authored-By: Claude Opus 5.5 --- dotnet-tools.json | 2 +- src/MesApp.Api/MesApp.Api.csproj | 10 +++++----- src/MesApp.Client.Web/MesApp.Client.Web.csproj | 8 ++++---- src/MesApp.Core/MesApp.Core.csproj | 2 +- src/MesApp.Infrastructure/MesApp.Infrastructure.csproj | 8 +++++--- tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/dotnet-tools.json b/dotnet-tools.json index cce89d8..39007e0 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "10.0.10", + "version": "10.0.13", "commands": [ "dotnet-ef" ], diff --git a/src/MesApp.Api/MesApp.Api.csproj b/src/MesApp.Api/MesApp.Api.csproj index 10f09cb..228060b 100644 --- a/src/MesApp.Api/MesApp.Api.csproj +++ b/src/MesApp.Api/MesApp.Api.csproj @@ -12,14 +12,14 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/MesApp.Client.Web/MesApp.Client.Web.csproj b/src/MesApp.Client.Web/MesApp.Client.Web.csproj index 8e0565a..0f9138e 100644 --- a/src/MesApp.Client.Web/MesApp.Client.Web.csproj +++ b/src/MesApp.Client.Web/MesApp.Client.Web.csproj @@ -9,10 +9,10 @@ - - - - + + + + diff --git a/src/MesApp.Core/MesApp.Core.csproj b/src/MesApp.Core/MesApp.Core.csproj index 87007c0..ee850e5 100644 --- a/src/MesApp.Core/MesApp.Core.csproj +++ b/src/MesApp.Core/MesApp.Core.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj b/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj index df4494a..250f842 100644 --- a/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj +++ b/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj @@ -5,11 +5,13 @@ - - - + + + + + diff --git a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj index d01b176..bb1decf 100644 --- a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj +++ b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj @@ -9,7 +9,7 @@ - + From 9de7a7cea156a237f33355fd79b2d09d5076556b Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Fri, 23 Oct 2026 07:13:24 +0900 Subject: [PATCH 02/11] =?UTF-8?q?=E4=BF=9D=E5=85=A8=E5=AE=9F=E7=B8=BE?= =?UTF-8?q?=E3=81=AE=E7=99=BB=E9=8C=B2=E3=82=92=E4=BF=9D=E5=85=A8=E6=8B=85?= =?UTF-8?q?=E5=BD=93=E8=80=85=E3=81=AB=E9=99=90=E3=82=8B=EF=BC=88E-40-30-0?= =?UTF-8?q?1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 単票API(POST api/maintenance-orders/{id}/record)と実績CSV maintenance-records が 全ロールに開いており、物流・品質の担当者でも在庫の引落し・保全指示の完了・ 設備の稼働可能への復帰・治工具の寿命リセットができた。 Spec.md 7.4 の「異常・使用実績の記録は絞らない」は保全依頼(起票)までで、実績は含まない。 - API・CSVとも MesRoleGroups.MaintenanceManage で絞る - 画面の実績登録フォームも同じ定数で隠す(取消ボタンと同じ範囲にまとめた) - 作業者が単票・CSVのどちらでも登録できず、指示が完了しないことをテストで確認 - Spec.md 改訂144 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 7 ++++--- .../Controllers/MaintenanceOrdersController.cs | 2 ++ src/MesApp.Api/Services/ActualCsvKinds.cs | 2 +- .../Pages/Maintenance/MaintenanceOrdersTab.razor | 7 ++++--- tests/MesApp.Api.Tests/MaintenanceTests.cs | 15 ++++++++++++++- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Spec.md b/Spec.md index 0b30ee7..ac6a036 100644 --- a/Spec.md +++ b/Spec.md @@ -134,6 +134,7 @@ 改訂133: 2026-09-23(**標準時間の見直し候補を生産性モニタリングに追加した**(`api/productivity/standard-time-review`。B-60-10-05)。標準時間の予実は作業指示ごとに並ぶだけで、工順マスタを見直すきっかけにつながっていなかった(実践的MES導入ガイド 8.3.2(4):標準時間などの業務パラメータは定期的に再設計する)。工順の工程ごとに実績の中央値をマスタの現在値と比べ、件数としきい値で候補を挙げる。マスタは書き換えない。新規エンティティは無い。3.2節・6.1節に反映) 改訂134: 2026-09-23(1.3節に**エネルギー管理を[将来拡張]とする**ことを明記した。仕様にエネルギーの記述が無く、対象かどうかが読めなかった(実践的MES導入ガイド 3.4.2・4.4.7・7.2.2 は、MES の役割を FEMS へ製造オーダ別の分母を渡すことと位置づける)。計測・総量監視・ユーティリティ制御は FEMS/FMCS の領域として持たず、実装する場合は設備×期間の使用量をCSVで受け取り稼働区間の時間比で作業指示へ按分する形とした。機能の追加・変更は無い) 改訂135: 2026-09-23(**検査項目・基準に必要スキル・資格を持たせ、検査実績の登録時に検査員と照合する**ようにした(`InspectionItem.RequiredSkillId`。F-20-30-01)。改訂130で保全を実装した際、検査の見直し(改訂125〜128)と衝突するため後回しにしていたもの。必要スキルは発行時に `InspectionOrderItem` へ写し(作業指示の必要スキルと同じ)、発行後にマスタを変えても発行済みの検査の照合条件は変わらない。写す値なので、CSV取込で変えると版数が上がる。判定は差立・着手・保全と同じ `SkillQualificationPolicy`。実績の訂正では照合しない。マスタCSVの検査項目に `RequiredSkillCode` 列を追加(列が無ければ現在値のまま)。3.6節・5.1節・5.4節に反映) +改訂144: 2026-10-23(7.4節・3.x節保全実施・3.8節⑨:**保全実績の登録を保全担当者(`MaintenanceManage`)に限った**。単票API(`POST api/maintenance-orders/{id}/record`)と実績CSV `maintenance-records` が全ロールに開いており、物流・品質の担当者でも在庫の引落し・保全指示の完了・設備の復帰・治工具の寿命リセットができた。7.4節の「異常・使用実績の記録は絞らない」は保全依頼(起票)までで、実績は含まない。画面の実績登録フォームも同じ定数で隠す) 改訂143: 2026-09-24(3.8節:実績CSVに保全計画 `maintenance-plans` を追加した(CSV対応バックログで保留にしていたもの)。計画の登録を `MaintenancePlansController.Create` から `MaintenanceOrderService.CreatePlanAsync` へ移し、単票APIとCSV取込の両方から呼ぶ(API契約は変えない)。計画には番号が無いため、保全指示CSVに `FromPlan` 列を足し、設備と予定日が一致する未指示の計画が1件だけのときにその計画から指示を作る。サンプル `samples/actual-csv/18_maintenance-plans.csv` を追加し、以降のサンプルの番号を1つずつ繰り下げた(19〜30)) 改訂142: 2026-09-24(3.8節:実績CSVに不適合 `nonconformances`・サンプル品保管 `sample-storages` を追加した(CSV-07)。不適合の対応と保管サンプルが画面からしか進められなかった。検査の不合格で自動起票された不適合は、ロット番号で指す(そのロットの未完了の不適合が1件だけのときに限る)。サンプルの採取・保管の終了を `SampleStoragesController` から新設の `SampleStorageService` へ移し、単票APIとCSV取込の両方から呼ぶ(API契約は変えない。サンプル番号の手入力はCSVのみ)。サンプル `samples/actual-csv/28〜29` を追加) 改訂141: 2026-09-24(3.8節:実績CSVに搬送指示 `transfer-orders`・ピッキング指示 `picking-orders`・棚卸指示 `stocktakes`・実棚数 `stocktake-counts` を追加した(CSV-06)。物流の指示系と倉庫業務進捗がサンプルで空のままだった。搬送指示の作成・実行・取消を `TransferOrdersController` から新設の `TransferOrderService` へ移し、単票APIとCSV取込の両方から呼ぶ(API契約は変えない)。ピッキング・棚卸は後続の行・ファイルから指すため番号を手入力にする(CSV-01 の手入力番号)。サンプル `samples/actual-csv/24〜27` を追加) @@ -323,7 +324,7 @@ AGV/AMR連携(D-50-20-02)・リサイクル管理(D-50-30-02)・VMI/外 - 同じ設備の同じ時間帯に複数の作業指示が紐づく場合(まとめ処理。3.9節)は重なりを**按分**する。1つの作業指示が複数設備で動いた場合は産出を稼働時間の比で配分する - 性能稼働率は100%を超えることがある(標準作業時間より速い)。上限で丸めない - **保全計画・指示**:中長期・年次保全計画の作成・変更(E-30-10-01〜03)、保全指示の作成・発行(E-30-20-01)、生産計画への影響連携(E-30-20-02)、計画外の保全依頼(E-30-30-01)。保全指示を発行済みの計画は取り消せない(先に保全指示を取り消す。指示の取消で計画は計画中へ戻る) -- **保全実施**:保全手順確認(E-40-10-01)、作業準備・実施(E-40-20-01〜02)、保全実績登録(消費部材・交換部品・対応人員等。E-40-30-01。設備が保全中なら稼働可能へ戻す。保全中への切替は保全担当が設備台帳で行う)。**消費部材はロットを指定して在庫から引き落とす**(引落しは在庫サービス経由。5.7節) +- **保全実施**:保全手順確認(E-40-10-01)、作業準備・実施(E-40-20-01〜02)、保全実績登録(消費部材・交換部品・対応人員等。E-40-30-01。**登録は保全担当者(`MaintenanceManage`)に限る**。設備が保全中なら稼働可能へ戻す。保全中への切替は保全担当が設備台帳で行う)。**消費部材はロットを指定して在庫から引き落とす**(引落しは在庫サービス経由。5.7節) - **消耗材モニタリング**:期間内に保全で消費した部材を品目別に集計し、現在庫と並べて補充の要否を判断する(E-20-10-04) - **治工具管理**:治工具マスタと在庫管理(E-60-10-01/03/04。在庫オペレーションと共通)、**寿命管理**(作業指示との紐付けによる利用実績記録(E-60-20-01)、閾値到達前の交換・廃棄通知(E-60-20-02)、**寿命分析**(E-60-20-03。期間内の使用ペースから寿命到達の見込み日を出す。使用実績のあった日で割り、閾値未設定・使用なし・到達済みは見込みを出さない))、治工具メンテナンスの指示・実施・実績(E-60-30-01〜03) - 予兆保全(E-20-20-01)は[将来拡張] @@ -360,7 +361,7 @@ AGV/AMR連携(D-50-20-02)・リサイクル管理(D-50-30-02)・VMI/外 - 監査ログ(実績訂正・検査訂正・マスタ変更等の変更履歴。7.6節) - **マスタのCSV一括入出力**:各マスタ(品目/MBOM/工順/工程/作業区/設備/設備の保全部品/治工具/ロケーション/検査項目・基準/**検査機・測定器**/工程管理項目/チェックリスト/作業手順書/勤務シフト/スキル・資格/ユーザー/ユーザーのスキル資格/**保全手順書**)のCSV出力・テンプレート出力・一括取込。保全手順書(`maintenance-procedures`)は単票APIと同じく保全の権限(`MaintenanceManage`)で取り込み、取込欄は保全管理画面の保全手順書タブに置く(一括ZIPでは最後の番号。対象設備・治工具・必要スキルをコードで参照するため)。取込はコード(自然キー)によるupsertで、他マスタはコードで参照する。文字コードはUTF-8/Shift_JISを自動判別し、出力はExcel互換(UTF-8 BOM+CRLF)。行単位で検証し、**1行でもエラーがあれば全件ロールバック**して行番号付きのエラー一覧を返す。反映せず検証のみ実行するモードを持つ。**参照されている状態のマスタを無効化できないという制約は、単票APIと同じ判定をCSV取込にも通す**(工順から参照中の作業手順書、在籍中の従業員が所属する直など)。片方にしか書かないとCSVから迂回できてしまうため、判定はポリシーへ集約する。なお**マスタの無効化は各マスタ画面から、再有効化はCSV取込の `IsActive` 列から**行う(全マスタ共通。画面に再有効化の導線は持たせていない) - **生産計画(`production-plans`)は取込の仕組みだけをこれに乗せる**。生産計画はマスタではなく日付を持つ業務データだが、改訂のたびに同じ計画を上書きしたいので、常に新規登録する実績CSVではなくキーで上書きするマスタCSVの取込を使う(A-30-10-01)。キーは `BusinessDate, ProductCode, ProcessCode, WorkCenterCode(任意)` で、同じキーなら計画数量(と `Note` 列があれば備考)を上書きし、無ければ追加する。**`WorkCenterCode` が空の行も1つのキーとして扱い**、同じ製造日・品目・工程の作業区なしの計画を上書きする(二重に追加しない)。計画数量は0以上。ファイル内で同じキーが複数行にあればエラー。取り込んだ後に同じ製造日・品目・工程で作業区の範囲が重なる行(既存の計画との間・ファイル内とも)もエラー。数量とキーの判定は単票API(`api/production-plans`)と共通の `ProductionPlanPolicy` に置く。取込の権限は単票の登録と同じ `ProductionManage`(種別の `WriteRoles`)。取り込み直しても変わらない行は更新日時を動かさない。**マスタではないので一括ZIPの取込・出力には含めない**(日々増えるデータを、マスタの移行や全件出力に巻き込まないため。ZIPに入っていれば取込を拒む)。取込は生産計画画面の計画登録タブから1ファイルずつ行い、出力は一覧と同じ絞り込み(製造日の期間・品目・工程・作業区)の `api/production-plans/csv` で行う -- **実績のCSV一括取込**:試験・移行・教育用に、実績(取引データ)をCSVで一括登録する(`api/actuals/csv/{kind}`。テンプレート出力と取込。対応種別は受入 `receiving`・製造指図 `manufacturing-orders`・段取り実績 `setup-records`・チェックリスト実施 `checklist-records`・部材投入 `consumptions`・生産実績 `production-records`・製造条件データ `data-records`・検査 `inspections`・作業時間 `work-time-records`・製造トラブル報告 `trouble-reports`・設備稼働記録 `equipment-logs`・出荷指示 `shipping-orders`・出荷判定 `shipment-judgments`・出荷実行 `shipments`・保全計画 `maintenance-plans`・保全指示 `maintenance-orders`・保全実績 `maintenance-records`・治工具の利用実績 `tool-usages`・治工具の引当 `tool-issues`・検査機の校正 `calibrations`・在庫オペレーション `inventory-operations`・搬送指示 `transfer-orders`・ピッキング指示 `picking-orders`・棚卸指示 `stocktakes`・実棚数 `stocktake-counts`・不適合 `nonconformances`・サンプル品保管 `sample-storages`)。マスタCSVと同じく**1行でもエラーがあれば全件ロールバック**し、行番号付きのエラー一覧を返し、検証のみのモードを持つ。マスタCSVとの違いは次のとおり。①**取込は常に新規登録**で既存の実績を更新しない(実績の訂正は理由付きの訂正機能で行う)。②行ごとに**単票APIと同じ業務サービスを通す**(受入は `ReceivingService`、製造指図は `ManufacturingOrderService`、作業指示の実行記録は `WorkOrderExecutionService`、検査は `InspectionService`、作業時間・トラブル報告は `ShopFloorReportService`、出荷指示・出荷実行は `ShippingService`、出荷判定は `ShipmentJudgmentService`、保全指示・保全実績は `MaintenanceOrderService`、治工具の利用実績は `ShopFloorReportService`、治工具の引当は `ToolIssueService`、校正は `InspectionDeviceService`、在庫オペレーションは `LotOperationService`、搬送は `TransferOrderService`、ピッキングは `PickingService`、棚卸は `StocktakeService`、不適合は `NonconformanceService`、サンプル品保管は `SampleStorageService`)——実績は在庫・ロット・状態履歴を連鎖して動かすため、行をエンティティへ直接写すと判定や監査ログが迂回される。③取込の権限は**種別ごとに単票APIと同じロールグループ**(受入は在庫・物流オペレーション)。④行は保存しながら進めるので、後の行は前の行の結果(登録済みロット番号の重複判定など)を前提にでき、検証のみも実際に登録してからロールバックする。他のマスタ・実績はIDではなくコード・番号で参照する。⑤**作業指示は「指図番号+工程順序」で指す**。自動採番の指図番号は取込前に分からないため、製造指図CSVに限り指図番号の手入力を許す(自動採番の形式 `MO〜` と重なる番号は、採番テーブルの連番と後で衝突するため拒否)。製造指図CSVは1行で登録・承認・工程展開まで進められるが、承認は省略できない(展開は承認済みの指図だけ)。⑥**実行記録の記録者は取込を実行したユーザー**とし、作業者を指定する列は持たない(他人名義の実績を作れると、記録者に意味が無くなる)。投入ロットはロット番号で、日時はオフセットの無い値を工場の時刻として読む。生産実績の直は開始日時で決まる。⑦**検査CSVは `InspectionKey` が同じ行を1件の検査指示にまとめる**。キーはファイルの中だけの名前でDBに残さない(検査指示番号は自動採番のままにし、後続のCSVから検査指示を指す必要も無いため)。まとまりに含まれる検査項目がその指示の対象になり、`Judge=true` なら実績に続けて総合判定する(ロットの状態反映・不合格時の不適合起票も単票APIと同じ)。⑧**出荷は指示・判定・実行の3種別に分ける**(判定は品質保証、指示と実行は物流の権限で、1種別にまとめると③を満たせない)。出荷指示CSVは出荷番号が同じ行を1件の指示(1行=1品目の明細。同じ品目を2行書けない)にまとめ、後続の判定・実行CSVから番号で指せるよう**出荷番号を必須の手入力**とする(⑤と同じ理由で自動採番の形式 `SH〜` と重なる番号は拒否)。出荷判定CSVは1行=1件の判定で、`Approve=true` なら続けて承認する。出荷実行CSVは出荷番号が同じ行を1回の出荷にまとめ、**承認済みの判定(可・特採)とロットの使用可否は単票APIと同じく出荷実行の処理で判定する**(判定の無い指示を含むファイルは全件取り消し)。⑨**保全は指示と実績の2種別に分ける**。保全指示CSVは1行=1件の指示で、保全実績CSVから指せるよう**保全指示番号を必須の手入力**とする(⑤と同じ理由で `MT〜` は拒否)。単票APIと同じく突発依頼は誰でも、計画保全の指示は保全担当者(`MaintenanceOrderService.PlannedOrderRoles`)だけが登録でき、CSVでは行ごとに取り込んだユーザーのロールで判定する。**保全計画CSV**は1行=1件の計画で常に新規登録する(設備×年度×区分でも同じ区分の計画が複数ありうるため自然なキーが無く、マスタCSVのように上書きしない。権限は単票と同じ `MaintenanceManage`)。計画には番号が無いので、保全指示CSVの `FromPlan=true` の行は**設備と予定日が一致する未指示の計画が1件だけのとき**にその計画から作り、計画を指示済みにする(0件・2件以上は行エラー。同じ設備の計画は予定日を分ける)。保全実績CSVは保全指示番号が同じ行を1件の実績にまとめ、1行に消費部材を1つ(ロット番号・払出元・数量)書ける。実施者は取り込んだユーザーで、手順書の必要スキルもこのユーザーと照合する。登録で指示は完了するので、1件の指示に実績は1件だけ。資産管理部品を書いた行・在庫不足は単票と同じく拒否する。⑩**番号の無い記録は1行で完結させる**。治工具の引当CSVは1行=1件の引当で、`Issue`・`Return` の列で払出・返却まで進め、後のファイルからは指さない(引当・払出の両方で `ToolIssuePolicy` が使用中・メンテ中・寿命到達を拒否する)。治工具の利用実績CSVは記録日時(`RecordedAt`)を指定でき、空欄なら取り込んだ時刻とする(寿命の累計はメンテで寿命をリセットした時刻より後の記録だけを数えるので、リセットより前の日時の記録は累計に入らない)。検査機の校正CSVは1行=1回の校正で、単票と同じく検査機の最終校正日・次回期限(省略時は校正周期から)を更新する。⑪**在庫オペレーションは1種別にまとめ、`Operation` 列で操作を選ぶ**(移動・数量調整・状態変更・分割・統合・振替・廃棄・返品・払出戻し。操作ごとに種別を分けると取込画面のパネルが9枚増えるため)。1行=1操作で、単票と同じ `LotOperationService` の同名メソッドを呼ぶ。操作ごとの必須列はコードで検査する(数量調整の理由は単票でも必須)。分割・振替は新ロット番号(`NewLotNumber`)を指定できるので、同じファイルの後の行から新しいロットを指せる。ロット系譜・状態履歴・監査ログは単票と同じに残る。⑫**物流の指示**:搬送指示CSVは番号が無いので1行=1件で、`Execute` の列で移動の実行まで進める。ピッキング指示CSVはピッキング番号(必須の手入力。`PK〜` は拒否)が同じ行を1件の指示にまとめ、1行=1明細(品目と数量)。ロット・ロケーションは単票と同じく先入れ先出し(有効期限優先。期限切れは引き当てない)で自動引当し、`Execute` で払出まで進める。棚卸指示CSVは棚卸番号(必須の手入力。`ST〜` は拒否)と対象ロケーションで作り、作成時点の在庫が明細になる。実棚数CSVは棚卸番号と明細のロット番号+ロケーションで指し、`Finalize` で確定(差異の棚卸調整)まで進める。⑬**不適合CSVは1行=1件の不適合**で、不適合番号(未登録なら新規起票。`NC〜` は拒否)か、番号を空欄にしてロット番号で指し、`Action`(対応指示)→`ActionRecord`(対応記録)→`Approve`(承認)の列があればその順に進める。検査の不合格で自動起票された不適合は番号が取り込むまで分からないため、**ロット番号で指せるのはそのロットの未完了(クローズ前)の不適合が1件だけのとき**に限る(0件・2件以上は行エラー。どれを進めるかを推測で決めない)。起票と対応記録は誰でも、対応指示と承認は単票と同じく品質管理の担当者だけ(行ごとに取り込んだユーザーのロールで判定)。⑭**サンプル品保管CSV**は1行=1件で、未登録のサンプル番号(`SP〜` は拒否)なら採取(在庫から抜く)、`Close`(払出・廃棄)を書けば保管を終える。登録済みの番号の行は保管の終了だけを行う +- **実績のCSV一括取込**:試験・移行・教育用に、実績(取引データ)をCSVで一括登録する(`api/actuals/csv/{kind}`。テンプレート出力と取込。対応種別は受入 `receiving`・製造指図 `manufacturing-orders`・段取り実績 `setup-records`・チェックリスト実施 `checklist-records`・部材投入 `consumptions`・生産実績 `production-records`・製造条件データ `data-records`・検査 `inspections`・作業時間 `work-time-records`・製造トラブル報告 `trouble-reports`・設備稼働記録 `equipment-logs`・出荷指示 `shipping-orders`・出荷判定 `shipment-judgments`・出荷実行 `shipments`・保全計画 `maintenance-plans`・保全指示 `maintenance-orders`・保全実績 `maintenance-records`・治工具の利用実績 `tool-usages`・治工具の引当 `tool-issues`・検査機の校正 `calibrations`・在庫オペレーション `inventory-operations`・搬送指示 `transfer-orders`・ピッキング指示 `picking-orders`・棚卸指示 `stocktakes`・実棚数 `stocktake-counts`・不適合 `nonconformances`・サンプル品保管 `sample-storages`)。マスタCSVと同じく**1行でもエラーがあれば全件ロールバック**し、行番号付きのエラー一覧を返し、検証のみのモードを持つ。マスタCSVとの違いは次のとおり。①**取込は常に新規登録**で既存の実績を更新しない(実績の訂正は理由付きの訂正機能で行う)。②行ごとに**単票APIと同じ業務サービスを通す**(受入は `ReceivingService`、製造指図は `ManufacturingOrderService`、作業指示の実行記録は `WorkOrderExecutionService`、検査は `InspectionService`、作業時間・トラブル報告は `ShopFloorReportService`、出荷指示・出荷実行は `ShippingService`、出荷判定は `ShipmentJudgmentService`、保全指示・保全実績は `MaintenanceOrderService`、治工具の利用実績は `ShopFloorReportService`、治工具の引当は `ToolIssueService`、校正は `InspectionDeviceService`、在庫オペレーションは `LotOperationService`、搬送は `TransferOrderService`、ピッキングは `PickingService`、棚卸は `StocktakeService`、不適合は `NonconformanceService`、サンプル品保管は `SampleStorageService`)——実績は在庫・ロット・状態履歴を連鎖して動かすため、行をエンティティへ直接写すと判定や監査ログが迂回される。③取込の権限は**種別ごとに単票APIと同じロールグループ**(受入は在庫・物流オペレーション)。④行は保存しながら進めるので、後の行は前の行の結果(登録済みロット番号の重複判定など)を前提にでき、検証のみも実際に登録してからロールバックする。他のマスタ・実績はIDではなくコード・番号で参照する。⑤**作業指示は「指図番号+工程順序」で指す**。自動採番の指図番号は取込前に分からないため、製造指図CSVに限り指図番号の手入力を許す(自動採番の形式 `MO〜` と重なる番号は、採番テーブルの連番と後で衝突するため拒否)。製造指図CSVは1行で登録・承認・工程展開まで進められるが、承認は省略できない(展開は承認済みの指図だけ)。⑥**実行記録の記録者は取込を実行したユーザー**とし、作業者を指定する列は持たない(他人名義の実績を作れると、記録者に意味が無くなる)。投入ロットはロット番号で、日時はオフセットの無い値を工場の時刻として読む。生産実績の直は開始日時で決まる。⑦**検査CSVは `InspectionKey` が同じ行を1件の検査指示にまとめる**。キーはファイルの中だけの名前でDBに残さない(検査指示番号は自動採番のままにし、後続のCSVから検査指示を指す必要も無いため)。まとまりに含まれる検査項目がその指示の対象になり、`Judge=true` なら実績に続けて総合判定する(ロットの状態反映・不合格時の不適合起票も単票APIと同じ)。⑧**出荷は指示・判定・実行の3種別に分ける**(判定は品質保証、指示と実行は物流の権限で、1種別にまとめると③を満たせない)。出荷指示CSVは出荷番号が同じ行を1件の指示(1行=1品目の明細。同じ品目を2行書けない)にまとめ、後続の判定・実行CSVから番号で指せるよう**出荷番号を必須の手入力**とする(⑤と同じ理由で自動採番の形式 `SH〜` と重なる番号は拒否)。出荷判定CSVは1行=1件の判定で、`Approve=true` なら続けて承認する。出荷実行CSVは出荷番号が同じ行を1回の出荷にまとめ、**承認済みの判定(可・特採)とロットの使用可否は単票APIと同じく出荷実行の処理で判定する**(判定の無い指示を含むファイルは全件取り消し)。⑨**保全は指示と実績の2種別に分ける**。保全指示CSVは1行=1件の指示で、保全実績CSVから指せるよう**保全指示番号を必須の手入力**とする(⑤と同じ理由で `MT〜` は拒否)。単票APIと同じく突発依頼は誰でも、計画保全の指示は保全担当者(`MaintenanceOrderService.PlannedOrderRoles`)だけが登録でき、CSVでは行ごとに取り込んだユーザーのロールで判定する。**保全計画CSV**は1行=1件の計画で常に新規登録する(設備×年度×区分でも同じ区分の計画が複数ありうるため自然なキーが無く、マスタCSVのように上書きしない。権限は単票と同じ `MaintenanceManage`)。計画には番号が無いので、保全指示CSVの `FromPlan=true` の行は**設備と予定日が一致する未指示の計画が1件だけのとき**にその計画から作り、計画を指示済みにする(0件・2件以上は行エラー。同じ設備の計画は予定日を分ける)。保全実績CSVは保全指示番号が同じ行を1件の実績にまとめ、1行に消費部材を1つ(ロット番号・払出元・数量)書ける。実施者は取り込んだユーザーで、手順書の必要スキルもこのユーザーと照合する。権限は単票と同じ `MaintenanceManage`。登録で指示は完了するので、1件の指示に実績は1件だけ。資産管理部品を書いた行・在庫不足は単票と同じく拒否する。⑩**番号の無い記録は1行で完結させる**。治工具の引当CSVは1行=1件の引当で、`Issue`・`Return` の列で払出・返却まで進め、後のファイルからは指さない(引当・払出の両方で `ToolIssuePolicy` が使用中・メンテ中・寿命到達を拒否する)。治工具の利用実績CSVは記録日時(`RecordedAt`)を指定でき、空欄なら取り込んだ時刻とする(寿命の累計はメンテで寿命をリセットした時刻より後の記録だけを数えるので、リセットより前の日時の記録は累計に入らない)。検査機の校正CSVは1行=1回の校正で、単票と同じく検査機の最終校正日・次回期限(省略時は校正周期から)を更新する。⑪**在庫オペレーションは1種別にまとめ、`Operation` 列で操作を選ぶ**(移動・数量調整・状態変更・分割・統合・振替・廃棄・返品・払出戻し。操作ごとに種別を分けると取込画面のパネルが9枚増えるため)。1行=1操作で、単票と同じ `LotOperationService` の同名メソッドを呼ぶ。操作ごとの必須列はコードで検査する(数量調整の理由は単票でも必須)。分割・振替は新ロット番号(`NewLotNumber`)を指定できるので、同じファイルの後の行から新しいロットを指せる。ロット系譜・状態履歴・監査ログは単票と同じに残る。⑫**物流の指示**:搬送指示CSVは番号が無いので1行=1件で、`Execute` の列で移動の実行まで進める。ピッキング指示CSVはピッキング番号(必須の手入力。`PK〜` は拒否)が同じ行を1件の指示にまとめ、1行=1明細(品目と数量)。ロット・ロケーションは単票と同じく先入れ先出し(有効期限優先。期限切れは引き当てない)で自動引当し、`Execute` で払出まで進める。棚卸指示CSVは棚卸番号(必須の手入力。`ST〜` は拒否)と対象ロケーションで作り、作成時点の在庫が明細になる。実棚数CSVは棚卸番号と明細のロット番号+ロケーションで指し、`Finalize` で確定(差異の棚卸調整)まで進める。⑬**不適合CSVは1行=1件の不適合**で、不適合番号(未登録なら新規起票。`NC〜` は拒否)か、番号を空欄にしてロット番号で指し、`Action`(対応指示)→`ActionRecord`(対応記録)→`Approve`(承認)の列があればその順に進める。検査の不合格で自動起票された不適合は番号が取り込むまで分からないため、**ロット番号で指せるのはそのロットの未完了(クローズ前)の不適合が1件だけのとき**に限る(0件・2件以上は行エラー。どれを進めるかを推測で決めない)。起票と対応記録は誰でも、対応指示と承認は単票と同じく品質管理の担当者だけ(行ごとに取り込んだユーザーのロールで判定)。⑭**サンプル品保管CSV**は1行=1件で、未登録のサンプル番号(`SP〜` は拒否)なら採取(在庫から抜く)、`Close`(払出・廃棄)を書けば保管を終える。登録済みの番号の行は保管の終了だけを行う - **CSVのZIP一括取込・一括出力**:複数のCSVをまとめたZIPを1回で取り込む(マスタは `POST api/masters/csv/bundle`、実績は `POST api/actuals/csv/bundle`)。**ファイル名の昇順に取り込み、種別はファイル名の最初の「_」より後ろで決める**(`01_work-centers.csv` → `work-centers`。同じ種別を番号違いで複数入れてよい)。CSV以外のファイル(README.md等)は無視する。**全ファイルを1つのトランザクションで処理し、どれか1ファイルでもエラーがあれば全ファイルを取り消す**(後のファイルが前のファイルの登録結果を参照するのでファイルごとに保存はするが、途中まで反映された状態が残るとどこから取り込み直せばよいか分からなくなるため)。エラーが出たファイルより後は処理せず、未処理として名前を返す。**マスタと実績が混ざったZIPは受け付けない**(取込の権限も対象画面も異なるため、別のZIPにする)。取込には**ZIPに含まれる全種別の取込権限**が要り、1種別でも権限が無ければ何も取り込まない。種別が分からないファイルは取り込む前に拒否する。ZIP爆弾対策として、CSVの数・1ファイルと合計の展開後サイズに上限を設け、読みながら数えて超えたら打ち切る。一括出力(`GET api/masters/csv/bundle`)は全マスタを取り込む順の番号付きファイル名でZIPにし、そのまま一括取込に使える(参照を絞っている種別は権限が無ければ含めない。ユーザーはパスワードを出力しないため、新しいDBへ移すときはユーザー系のファイルに初期パスワードを書き足す)。実績の一括出力は持たない - バーコード/QRスキャン共通コンポーネント(ブラウザのカメラAPI or USB HIDリーダー入力) @@ -684,7 +685,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと - **参照系と更新系の線引き**:参照系は認証済みユーザー全員に開放し、更新系のみロールで絞る。ただし**コントローラのクラスに `[Authorize(Roles = ...)]` を付けてはならない**。認可属性はクラスとアクションで合成されるため、クラスで絞るとアクション側の `[Authorize]` では参照系を開放できない(出荷・出庫・棚卸で実際に、出荷判定を担う品質保証ロールが出荷指示を参照できなくなっていた)。クラスは `[Authorize]` とし、更新系のアクションへ個別にロールを付ける - **製造実行の記録の権限**:着手・段取り・チェックリスト・部材投入・実績報告・製造条件データ・作業時間の記録は、システム管理者・生産管理担当者・現場作業者に限る(`RoleGroups.ShopFloorRecord`)。現場作業者ロールの定義(1.2節)がまさにこの範囲であり、品質・保全・物流の担当者が製造実績を報告できる状態は監査上説明できないため - **CSV取込の権限は単票APIに揃える**:マスタCSVの取込に必要なロールは、そのマスタの単票API(`POST`/`PUT`)と同じにする。CSVは同じマスタへの別の入口であり、片方だけ緩いと「画面のフォームからは登録できないマスタが、CSVからは書き換えられる」状態になる(スキル・資格で実際に起きていた)。種別ごとの必要権限は`MasterCsvKinds`が持ち、`MasterCsvController`はそれを見て判定する -- **異常・使用実績の記録は絞らない**:設備稼働報告・治工具使用実績・保全依頼・不適合報告は、認証済みであればロールを問わず記録できる(上の「更新系はロールで絞る」の意図的な例外)。気づいた人がその場で上げられることを優先し、絞って報告が落ちるほうが害が大きいと判断する。これらに対する**指示・承認・判定は従来どおりロールで絞る** +- **異常・使用実績の記録は絞らない**:設備稼働報告・治工具使用実績・保全依頼・不適合報告は、認証済みであればロールを問わず記録できる(上の「更新系はロールで絞る」の意図的な例外)。気づいた人がその場で上げられることを優先し、絞って報告が落ちるほうが害が大きいと判断する。これらに対する**指示・承認・判定は従来どおりロールで絞る**。**保全実績の登録はこの例外に含めない**(保全依頼と違い、在庫の引落し・保全指示の完了・設備の稼働可能への復帰・治工具の寿命リセットまで動かす。報告ではなく作業の完了の記録なので、保全担当者 `MaintenanceManage` に限る) - **初回パスワード変更の強制**:初期管理者のシードおよび管理者によるユーザー作成・パスワードリセットで発行した資格情報は、パスワード変更が済むまでパスワード変更以外のAPIを使えない。アクセストークンに未変更を示すクレームを載せ、API側(`Api/MustChangePasswordFilter`)が参照系も含めて403で拒否する(クライアント側の画面誘導だけでは、デスクトップ配布で画面に表示する初期パスワード(7.8節)のまま全APIを操作できてしまうため) ### 7.5 性能・規模 diff --git a/src/MesApp.Api/Controllers/MaintenanceOrdersController.cs b/src/MesApp.Api/Controllers/MaintenanceOrdersController.cs index 0864e2b..3e88eb9 100644 --- a/src/MesApp.Api/Controllers/MaintenanceOrdersController.cs +++ b/src/MesApp.Api/Controllers/MaintenanceOrdersController.cs @@ -89,8 +89,10 @@ public async Task> Create( /// /// 保全実績の登録(E-40-30-01)。登録と同時に指示は完了。元計画は完了、 /// 治工具メンテでresetToolLife指定時は寿命カウンタをリセットする(E-60-30)。 + /// 在庫の引落し・設備の復帰・寿命のリセットまで動かすため、突発依頼と違い保全ロールに限る(Spec.md 7.4)。 /// [HttpPost("{id:int}/record")] + [Authorize(Roles = MesRoleGroups.MaintenanceManage)] public async Task> AddRecord( int id, MaintenanceRecordRequest request, CancellationToken ct) { diff --git a/src/MesApp.Api/Services/ActualCsvKinds.cs b/src/MesApp.Api/Services/ActualCsvKinds.cs index 955c020..a0281e8 100644 --- a/src/MesApp.Api/Services/ActualCsvKinds.cs +++ b/src/MesApp.Api/Services/ActualCsvKinds.cs @@ -238,7 +238,7 @@ public static class ActualCsvKinds new("PartLocationCode", "払出元ロケーションコード", false, "消費部材を書くときは必須"), new("PartQuantity", "消費数量", false, "消費部材を書くときは必須"), new("PartNote", "部材の備考", false, null), - ]), AnyRole), + ]), MesRoleGroups.MaintenanceManage), new(new CsvKindInfo(ToolUsages, "治工具の利用実績", false, [ new("ToolCode", "治工具コード", true, "登録済みで有効な治工具"), diff --git a/src/MesApp.Client.Web/Pages/Maintenance/MaintenanceOrdersTab.razor b/src/MesApp.Client.Web/Pages/Maintenance/MaintenanceOrdersTab.razor index 621b4fd..5c6dac5 100644 --- a/src/MesApp.Client.Web/Pages/Maintenance/MaintenanceOrdersTab.razor +++ b/src/MesApp.Client.Web/Pages/Maintenance/MaintenanceOrdersTab.razor @@ -121,8 +121,10 @@ } + @* 保全実績の登録・取消は保全ロールだけ(APIと同じ定数。突発依頼の起票は全ロールに開いている) *@ @if (order.Status == MaintenanceOrderStatus.Instructed) { +
@@ -184,10 +186,9 @@
- - - +
+ }
} diff --git a/tests/MesApp.Api.Tests/MaintenanceTests.cs b/tests/MesApp.Api.Tests/MaintenanceTests.cs index 2356d58..e7dc155 100644 --- a/tests/MesApp.Api.Tests/MaintenanceTests.cs +++ b/tests/MesApp.Api.Tests/MaintenanceTests.cs @@ -972,7 +972,7 @@ public async Task 指示発行済みの保全計画は指示を取り消すま } [Fact] - public async Task 突発の保全依頼は作業者も起票できるが計画保全は保全ロールのみ() + public async Task 突発の保全依頼は作業者も起票できるが計画保全と保全実績は保全ロールのみ() { using var factory = new ApiFactory(); using var admin = await TestAuth.CreateAdminClientAsync(factory); @@ -996,6 +996,19 @@ public async Task 突発の保全依頼は作業者も起票できるが計画 var plan = await operator_.PostAsJsonAsync("/api/maintenance-plans", new MaintenancePlanRequest(equipment.Id, MaintenanceCategory.Periodic, 2026, null, null, null)); Assert.Equal(HttpStatusCode.Forbidden, plan.StatusCode); + + // 保全実績の登録は保全ロールのみ(在庫の引落し・設備の復帰・寿命のリセットを動かすため)。単票もCSVも同じ + var order = await spot.Content.ReadFromJsonAsync(); + var record = await operator_.PostAsJsonAsync($"/api/maintenance-orders/{order!.Id}/record", + new MaintenanceRecordRequest(DateTimeOffset.Now.AddHours(-1), DateTimeOffset.Now, null, "点検完了", null)); + Assert.Equal(HttpStatusCode.Forbidden, record.StatusCode); + var csv = await Phase3TestData.PostActualCsvAsync(operator_, "maintenance-records", $""" + MaintenanceNo,StartedAt + {order.OrderNo},2026-09-24 09:00 + """); + Assert.Equal(HttpStatusCode.Forbidden, csv.StatusCode); + var unchanged = await admin.GetFromJsonAsync($"/api/maintenance-orders/{order.Id}"); + Assert.Equal(MaintenanceOrderStatus.Instructed, unchanged!.Status); } [Fact] From 547d5e1551407a4312effcfc3307f66f7109cd33 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Fri, 23 Oct 2026 08:08:37 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=E4=BE=9D=E5=AD=98=E3=83=91=E3=83=83?= =?UTF-8?q?=E3=82=B1=E3=83=BC=E3=82=B8=E3=81=AE=E6=9B=B4=E6=96=B0=E7=A2=BA?= =?UTF-8?q?=E8=AA=8D=E3=81=A8=E8=84=86=E5=BC=B1=E6=80=A7=E3=83=81=E3=82=A7?= =?UTF-8?q?=E3=83=83=E3=82=AF=E3=82=92=E5=AE=9A=E6=9C=9F=E5=AE=9F=E8=A1=8C?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dependabot(nuget・github-actions):毎週月曜 09:00(JST)に確認し、dev 向けの PR を作る。 担当者を自分に割り当て、GitHub の通知(Inbox)と Assigned の一覧に出す。 Microsoft.* / System.* は版ずれ(NU1605)を避けるため1つの PR にまとめる - Vulnerability ワークフロー:push・PR と週1回の定期実行で、推移的な依存も含めて 既知の脆弱性を一覧にし、見つかったら失敗させる。復元時の NuGet 監査は警告がエラー扱いで 一覧が出る前に落ちるため、復元では止めてこの手順で判定する Co-Authored-By: Claude Opus 5.5 --- .github/dependabot.yml | 51 ++++++++++++++++++++++ .github/workflows/vulnerability.yml | 66 +++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/vulnerability.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0031fbc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,51 @@ +# 依存パッケージの更新確認(週1回)。更新があれば dev 向けの PR を作る +# PR は担当者に自分を割り当て、GitHub の通知(Inbox)と「Assigned」の一覧に出す +version: 2 +updates: + - package-ecosystem: nuget + directory: / + target-branch: dev + schedule: + interval: weekly + day: monday + time: "09:00" + timezone: Asia/Tokyo + assignees: + - msmsrep + commit-message: + prefix: "依存更新" + groups: + # Microsoft.* / System.* は同じサービシングで揃えて上げる(版がずれると NU1605 の格下げエラーになる) + dotnet: + patterns: + - "Microsoft.*" + - "System.*" + update-types: + - minor + - patch + others: + patterns: + - "*" + exclude-patterns: + - "Microsoft.*" + - "System.*" + update-types: + - minor + - patch + + - package-ecosystem: github-actions + directory: / + target-branch: dev + schedule: + interval: weekly + day: monday + time: "09:00" + timezone: Asia/Tokyo + assignees: + - msmsrep + commit-message: + prefix: "依存更新" + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/vulnerability.yml b/.github/workflows/vulnerability.yml new file mode 100644 index 0000000..eeec040 --- /dev/null +++ b/.github/workflows/vulnerability.yml @@ -0,0 +1,66 @@ +# 依存パッケージの既知の脆弱性チェック(推移的な依存も含む)。見つかったら失敗させる +# コードを触っていない期間に公開された脆弱性にも気づけるよう、週1回の定期実行もかける +# (定期実行は既定ブランチ main で動く) +name: Vulnerability + +on: + push: + branches: [main, dev] + pull_request: + schedule: + # 毎週月曜 09:00(JST)。Dependabot の確認と同じ時刻 + - cron: "0 0 * * 1" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: vulnerability-${{ github.ref }} + cancel-in-progress: true + +jobs: + vulnerable-packages: + # ソリューションに MesApp.Desktop(net10.0-windows)を含むため Windows で復元する + runs-on: windows-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: 10.0.x + + # 警告をエラーにしているため、復元時の NuGet 監査(NU1901〜1904)で先に落ちて一覧が出ない。 + # 復元では監査を止め、次の手順でパッケージ・重大度・アドバイザリを一覧にして判定する + - name: 復元 + run: dotnet restore ParallelFactoryMES.slnx -p:NuGetAudit=false + + - name: 脆弱なパッケージの確認 + shell: pwsh + run: | + $json = dotnet list ParallelFactoryMES.slnx package --vulnerable --include-transitive --no-restore --format json | Out-String + if ($LASTEXITCODE -ne 0) { Write-Output $json; exit 1 } + $report = $json | ConvertFrom-Json + $found = foreach ($project in $report.projects) { + foreach ($framework in @($project.frameworks)) { + foreach ($package in @($framework.topLevelPackages) + @($framework.transitivePackages)) { + if (-not $package) { continue } + foreach ($v in @($package.vulnerabilities)) { + [pscustomobject]@{ + Project = Split-Path $project.path -Leaf + Package = $package.id + Version = $package.resolvedVersion + Severity = $v.severity + Advisory = $v.advisoryurl + } + } + } + } + } + if ($found) { + $found | Sort-Object Package, Advisory -Unique | Format-Table -AutoSize | Out-String -Width 300 | Write-Output + Write-Output "::error::既知の脆弱性があるパッケージが $(@($found | Sort-Object Package -Unique).Count) 件あります。修正版へ更新してください。" + exit 1 + } + Write-Output "脆弱なパッケージはありません。" From c526b0e3c35c7b2e3efd2cfefd4609c454b3188c Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:00:37 +0900 Subject: [PATCH 04/11] =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=BB=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=83=BC=E3=82=AF=E3=83=B3=E3=82=92=E7=84=A1=E5=8A=B9?= =?UTF-8?q?=E5=8C=96=E3=83=BB=E3=83=AD=E3=83=BC=E3=83=AB=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=A7=E5=8D=B3=E6=99=82=E5=A4=B1=E5=8A=B9=E3=81=95=E3=81=9B?= =?UTF-8?q?=E3=80=81CSV=E5=87=BA=E5=8A=9B=E3=81=AE=E6=95=B0=E5=BC=8F?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=B8=E3=82=A7=E3=82=AF=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=82=92=E9=98=B2=E3=81=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - アクセストークンに SecurityStamp を載せ、検証のたびにDBの値・有効フラグと照合する(Api/AccessTokenValidation)。 これまではリフレッシュトークンだけを失効させていたため、無効化・ロール変更後も最大60分は操作できた - 無効化・ロール変更で UpdateSecurityStampAsync を呼ぶ(単票API・ユーザーCSVの両方) - Identity のトークンプロバイダが未登録で、管理者のパスワードリセット(API・ユーザーCSVの InitialPassword・ 初期管理者の再生成)が例外になっていたのを AddDefaultTokenProviders で直した - CSV出力で先頭が = @ タブ CR、または数値でない + - の値に ' を付ける。取込では外す(出力をそのまま取り込み直せる) - Spec.md 7.4・3.8、改訂145 Co-Authored-By: Claude Opus 5.5 --- Spec.md | 4 +- src/MesApp.Api/AccessTokenValidation.cs | 43 ++++++++++++++ src/MesApp.Api/Controllers/UsersController.cs | 6 ++ src/MesApp.Api/MesAppHost.cs | 7 ++- src/MesApp.Api/MustChangePasswordFilter.cs | 4 +- src/MesApp.Api/Services/CsvFile.cs | 30 +++++++++- src/MesApp.Api/Services/JwtTokenService.cs | 2 + .../Services/MasterCsvService.Import.cs | 8 +++ src/MesApp.Core/Constants/MesClaimTypes.cs | 6 ++ tests/MesApp.Api.Tests/AuthTests.cs | 56 +++++++++++++++++++ tests/MesApp.Api.Tests/MasterCsvTests.cs | 33 +++++++++++ 11 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 src/MesApp.Api/AccessTokenValidation.cs diff --git a/Spec.md b/Spec.md index ac6a036..77e1c19 100644 --- a/Spec.md +++ b/Spec.md @@ -134,6 +134,7 @@ 改訂133: 2026-09-23(**標準時間の見直し候補を生産性モニタリングに追加した**(`api/productivity/standard-time-review`。B-60-10-05)。標準時間の予実は作業指示ごとに並ぶだけで、工順マスタを見直すきっかけにつながっていなかった(実践的MES導入ガイド 8.3.2(4):標準時間などの業務パラメータは定期的に再設計する)。工順の工程ごとに実績の中央値をマスタの現在値と比べ、件数としきい値で候補を挙げる。マスタは書き換えない。新規エンティティは無い。3.2節・6.1節に反映) 改訂134: 2026-09-23(1.3節に**エネルギー管理を[将来拡張]とする**ことを明記した。仕様にエネルギーの記述が無く、対象かどうかが読めなかった(実践的MES導入ガイド 3.4.2・4.4.7・7.2.2 は、MES の役割を FEMS へ製造オーダ別の分母を渡すことと位置づける)。計測・総量監視・ユーティリティ制御は FEMS/FMCS の領域として持たず、実装する場合は設備×期間の使用量をCSVで受け取り稼働区間の時間比で作業指示へ按分する形とした。機能の追加・変更は無い) 改訂135: 2026-09-23(**検査項目・基準に必要スキル・資格を持たせ、検査実績の登録時に検査員と照合する**ようにした(`InspectionItem.RequiredSkillId`。F-20-30-01)。改訂130で保全を実装した際、検査の見直し(改訂125〜128)と衝突するため後回しにしていたもの。必要スキルは発行時に `InspectionOrderItem` へ写し(作業指示の必要スキルと同じ)、発行後にマスタを変えても発行済みの検査の照合条件は変わらない。写す値なので、CSV取込で変えると版数が上がる。判定は差立・着手・保全と同じ `SkillQualificationPolicy`。実績の訂正では照合しない。マスタCSVの検査項目に `RequiredSkillCode` 列を追加(列が無ければ現在値のまま)。3.6節・5.1節・5.4節に反映) +改訂145: 2026-10-23(7.4節:**アクセストークンを無効化・ロール変更・パスワード変更で即時に失効させる**ことを追加した。これまでリフレッシュトークンだけを失効させていたため、発行済みのアクセストークンは有効期限(既定60分)まで使えた。トークンに Identity のセキュリティスタンプを載せ、検証のたびにDBの値・有効フラグと照合する(`Api/AccessTokenValidation`)。あわせて、Identity のトークンプロバイダが未登録で管理者のパスワードリセット(API・ユーザーCSVの `InitialPassword`・初期管理者の再生成)が例外になっていたのを直した。3.8節:**CSV出力の数式インジェクション対策**を追加した) 改訂144: 2026-10-23(7.4節・3.x節保全実施・3.8節⑨:**保全実績の登録を保全担当者(`MaintenanceManage`)に限った**。単票API(`POST api/maintenance-orders/{id}/record`)と実績CSV `maintenance-records` が全ロールに開いており、物流・品質の担当者でも在庫の引落し・保全指示の完了・設備の復帰・治工具の寿命リセットができた。7.4節の「異常・使用実績の記録は絞らない」は保全依頼(起票)までで、実績は含まない。画面の実績登録フォームも同じ定数で隠す) 改訂143: 2026-09-24(3.8節:実績CSVに保全計画 `maintenance-plans` を追加した(CSV対応バックログで保留にしていたもの)。計画の登録を `MaintenancePlansController.Create` から `MaintenanceOrderService.CreatePlanAsync` へ移し、単票APIとCSV取込の両方から呼ぶ(API契約は変えない)。計画には番号が無いため、保全指示CSVに `FromPlan` 列を足し、設備と予定日が一致する未指示の計画が1件だけのときにその計画から指示を作る。サンプル `samples/actual-csv/18_maintenance-plans.csv` を追加し、以降のサンプルの番号を1つずつ繰り下げた(19〜30)) 改訂142: 2026-09-24(3.8節:実績CSVに不適合 `nonconformances`・サンプル品保管 `sample-storages` を追加した(CSV-07)。不適合の対応と保管サンプルが画面からしか進められなかった。検査の不合格で自動起票された不適合は、ロット番号で指す(そのロットの未完了の不適合が1件だけのときに限る)。サンプルの採取・保管の終了を `SampleStoragesController` から新設の `SampleStorageService` へ移し、単票APIとCSV取込の両方から呼ぶ(API契約は変えない。サンプル番号の手入力はCSVのみ)。サンプル `samples/actual-csv/28〜29` を追加) @@ -359,7 +360,7 @@ AGV/AMR連携(D-50-20-02)・リサイクル管理(D-50-30-02)・VMI/外 - 生産実績も稼働ログも**開始時刻が属する製造日**で振り分ける(品質分析・生産性・OEEも同じ)。稼働は工程・品目では絞れない(作業指示との紐付けが任意のため)。生産実績は設備では分けられない(設備が作業指示に固定されないため)。求められない指標は0でなくnull(「実績なし」と区別する)。ライン軸は最下段の作業区を祖先のライン段へまとめる - 帳票・ラベル発行基盤(作業指示書・現品票・検査成績書・出荷伝票・出荷判定書・棚卸表等の印刷/PDF出力。B-10-30-04、C-20-10-05、D-10-10-03、D-50-30-05〜06、H-10-10-04) - 監査ログ(実績訂正・検査訂正・マスタ変更等の変更履歴。7.6節) -- **マスタのCSV一括入出力**:各マスタ(品目/MBOM/工順/工程/作業区/設備/設備の保全部品/治工具/ロケーション/検査項目・基準/**検査機・測定器**/工程管理項目/チェックリスト/作業手順書/勤務シフト/スキル・資格/ユーザー/ユーザーのスキル資格/**保全手順書**)のCSV出力・テンプレート出力・一括取込。保全手順書(`maintenance-procedures`)は単票APIと同じく保全の権限(`MaintenanceManage`)で取り込み、取込欄は保全管理画面の保全手順書タブに置く(一括ZIPでは最後の番号。対象設備・治工具・必要スキルをコードで参照するため)。取込はコード(自然キー)によるupsertで、他マスタはコードで参照する。文字コードはUTF-8/Shift_JISを自動判別し、出力はExcel互換(UTF-8 BOM+CRLF)。行単位で検証し、**1行でもエラーがあれば全件ロールバック**して行番号付きのエラー一覧を返す。反映せず検証のみ実行するモードを持つ。**参照されている状態のマスタを無効化できないという制約は、単票APIと同じ判定をCSV取込にも通す**(工順から参照中の作業手順書、在籍中の従業員が所属する直など)。片方にしか書かないとCSVから迂回できてしまうため、判定はポリシーへ集約する。なお**マスタの無効化は各マスタ画面から、再有効化はCSV取込の `IsActive` 列から**行う(全マスタ共通。画面に再有効化の導線は持たせていない) +- **マスタのCSV一括入出力**:各マスタ(品目/MBOM/工順/工程/作業区/設備/設備の保全部品/治工具/ロケーション/検査項目・基準/**検査機・測定器**/工程管理項目/チェックリスト/作業手順書/勤務シフト/スキル・資格/ユーザー/ユーザーのスキル資格/**保全手順書**)のCSV出力・テンプレート出力・一括取込。保全手順書(`maintenance-procedures`)は単票APIと同じく保全の権限(`MaintenanceManage`)で取り込み、取込欄は保全管理画面の保全手順書タブに置く(一括ZIPでは最後の番号。対象設備・治工具・必要スキルをコードで参照するため)。取込はコード(自然キー)によるupsertで、他マスタはコードで参照する。文字コードはUTF-8/Shift_JISを自動判別し、出力はExcel互換(UTF-8 BOM+CRLF)。**表計算ソフトで数式として実行される値(先頭が `=`・`@`・タブ・CR、または数値でない `+`・`-`)は前に `'` を付けて出力する**(CSVインジェクション対策。品目名・備考など現場の入力がそのまま出るため)。取込では同じ印を外すので、出力したCSVをそのまま取り込み直しても値は変わらない。行単位で検証し、**1行でもエラーがあれば全件ロールバック**して行番号付きのエラー一覧を返す。反映せず検証のみ実行するモードを持つ。**参照されている状態のマスタを無効化できないという制約は、単票APIと同じ判定をCSV取込にも通す**(工順から参照中の作業手順書、在籍中の従業員が所属する直など)。片方にしか書かないとCSVから迂回できてしまうため、判定はポリシーへ集約する。なお**マスタの無効化は各マスタ画面から、再有効化はCSV取込の `IsActive` 列から**行う(全マスタ共通。画面に再有効化の導線は持たせていない) - **生産計画(`production-plans`)は取込の仕組みだけをこれに乗せる**。生産計画はマスタではなく日付を持つ業務データだが、改訂のたびに同じ計画を上書きしたいので、常に新規登録する実績CSVではなくキーで上書きするマスタCSVの取込を使う(A-30-10-01)。キーは `BusinessDate, ProductCode, ProcessCode, WorkCenterCode(任意)` で、同じキーなら計画数量(と `Note` 列があれば備考)を上書きし、無ければ追加する。**`WorkCenterCode` が空の行も1つのキーとして扱い**、同じ製造日・品目・工程の作業区なしの計画を上書きする(二重に追加しない)。計画数量は0以上。ファイル内で同じキーが複数行にあればエラー。取り込んだ後に同じ製造日・品目・工程で作業区の範囲が重なる行(既存の計画との間・ファイル内とも)もエラー。数量とキーの判定は単票API(`api/production-plans`)と共通の `ProductionPlanPolicy` に置く。取込の権限は単票の登録と同じ `ProductionManage`(種別の `WriteRoles`)。取り込み直しても変わらない行は更新日時を動かさない。**マスタではないので一括ZIPの取込・出力には含めない**(日々増えるデータを、マスタの移行や全件出力に巻き込まないため。ZIPに入っていれば取込を拒む)。取込は生産計画画面の計画登録タブから1ファイルずつ行い、出力は一覧と同じ絞り込み(製造日の期間・品目・工程・作業区)の `api/production-plans/csv` で行う - **実績のCSV一括取込**:試験・移行・教育用に、実績(取引データ)をCSVで一括登録する(`api/actuals/csv/{kind}`。テンプレート出力と取込。対応種別は受入 `receiving`・製造指図 `manufacturing-orders`・段取り実績 `setup-records`・チェックリスト実施 `checklist-records`・部材投入 `consumptions`・生産実績 `production-records`・製造条件データ `data-records`・検査 `inspections`・作業時間 `work-time-records`・製造トラブル報告 `trouble-reports`・設備稼働記録 `equipment-logs`・出荷指示 `shipping-orders`・出荷判定 `shipment-judgments`・出荷実行 `shipments`・保全計画 `maintenance-plans`・保全指示 `maintenance-orders`・保全実績 `maintenance-records`・治工具の利用実績 `tool-usages`・治工具の引当 `tool-issues`・検査機の校正 `calibrations`・在庫オペレーション `inventory-operations`・搬送指示 `transfer-orders`・ピッキング指示 `picking-orders`・棚卸指示 `stocktakes`・実棚数 `stocktake-counts`・不適合 `nonconformances`・サンプル品保管 `sample-storages`)。マスタCSVと同じく**1行でもエラーがあれば全件ロールバック**し、行番号付きのエラー一覧を返し、検証のみのモードを持つ。マスタCSVとの違いは次のとおり。①**取込は常に新規登録**で既存の実績を更新しない(実績の訂正は理由付きの訂正機能で行う)。②行ごとに**単票APIと同じ業務サービスを通す**(受入は `ReceivingService`、製造指図は `ManufacturingOrderService`、作業指示の実行記録は `WorkOrderExecutionService`、検査は `InspectionService`、作業時間・トラブル報告は `ShopFloorReportService`、出荷指示・出荷実行は `ShippingService`、出荷判定は `ShipmentJudgmentService`、保全指示・保全実績は `MaintenanceOrderService`、治工具の利用実績は `ShopFloorReportService`、治工具の引当は `ToolIssueService`、校正は `InspectionDeviceService`、在庫オペレーションは `LotOperationService`、搬送は `TransferOrderService`、ピッキングは `PickingService`、棚卸は `StocktakeService`、不適合は `NonconformanceService`、サンプル品保管は `SampleStorageService`)——実績は在庫・ロット・状態履歴を連鎖して動かすため、行をエンティティへ直接写すと判定や監査ログが迂回される。③取込の権限は**種別ごとに単票APIと同じロールグループ**(受入は在庫・物流オペレーション)。④行は保存しながら進めるので、後の行は前の行の結果(登録済みロット番号の重複判定など)を前提にでき、検証のみも実際に登録してからロールバックする。他のマスタ・実績はIDではなくコード・番号で参照する。⑤**作業指示は「指図番号+工程順序」で指す**。自動採番の指図番号は取込前に分からないため、製造指図CSVに限り指図番号の手入力を許す(自動採番の形式 `MO〜` と重なる番号は、採番テーブルの連番と後で衝突するため拒否)。製造指図CSVは1行で登録・承認・工程展開まで進められるが、承認は省略できない(展開は承認済みの指図だけ)。⑥**実行記録の記録者は取込を実行したユーザー**とし、作業者を指定する列は持たない(他人名義の実績を作れると、記録者に意味が無くなる)。投入ロットはロット番号で、日時はオフセットの無い値を工場の時刻として読む。生産実績の直は開始日時で決まる。⑦**検査CSVは `InspectionKey` が同じ行を1件の検査指示にまとめる**。キーはファイルの中だけの名前でDBに残さない(検査指示番号は自動採番のままにし、後続のCSVから検査指示を指す必要も無いため)。まとまりに含まれる検査項目がその指示の対象になり、`Judge=true` なら実績に続けて総合判定する(ロットの状態反映・不合格時の不適合起票も単票APIと同じ)。⑧**出荷は指示・判定・実行の3種別に分ける**(判定は品質保証、指示と実行は物流の権限で、1種別にまとめると③を満たせない)。出荷指示CSVは出荷番号が同じ行を1件の指示(1行=1品目の明細。同じ品目を2行書けない)にまとめ、後続の判定・実行CSVから番号で指せるよう**出荷番号を必須の手入力**とする(⑤と同じ理由で自動採番の形式 `SH〜` と重なる番号は拒否)。出荷判定CSVは1行=1件の判定で、`Approve=true` なら続けて承認する。出荷実行CSVは出荷番号が同じ行を1回の出荷にまとめ、**承認済みの判定(可・特採)とロットの使用可否は単票APIと同じく出荷実行の処理で判定する**(判定の無い指示を含むファイルは全件取り消し)。⑨**保全は指示と実績の2種別に分ける**。保全指示CSVは1行=1件の指示で、保全実績CSVから指せるよう**保全指示番号を必須の手入力**とする(⑤と同じ理由で `MT〜` は拒否)。単票APIと同じく突発依頼は誰でも、計画保全の指示は保全担当者(`MaintenanceOrderService.PlannedOrderRoles`)だけが登録でき、CSVでは行ごとに取り込んだユーザーのロールで判定する。**保全計画CSV**は1行=1件の計画で常に新規登録する(設備×年度×区分でも同じ区分の計画が複数ありうるため自然なキーが無く、マスタCSVのように上書きしない。権限は単票と同じ `MaintenanceManage`)。計画には番号が無いので、保全指示CSVの `FromPlan=true` の行は**設備と予定日が一致する未指示の計画が1件だけのとき**にその計画から作り、計画を指示済みにする(0件・2件以上は行エラー。同じ設備の計画は予定日を分ける)。保全実績CSVは保全指示番号が同じ行を1件の実績にまとめ、1行に消費部材を1つ(ロット番号・払出元・数量)書ける。実施者は取り込んだユーザーで、手順書の必要スキルもこのユーザーと照合する。権限は単票と同じ `MaintenanceManage`。登録で指示は完了するので、1件の指示に実績は1件だけ。資産管理部品を書いた行・在庫不足は単票と同じく拒否する。⑩**番号の無い記録は1行で完結させる**。治工具の引当CSVは1行=1件の引当で、`Issue`・`Return` の列で払出・返却まで進め、後のファイルからは指さない(引当・払出の両方で `ToolIssuePolicy` が使用中・メンテ中・寿命到達を拒否する)。治工具の利用実績CSVは記録日時(`RecordedAt`)を指定でき、空欄なら取り込んだ時刻とする(寿命の累計はメンテで寿命をリセットした時刻より後の記録だけを数えるので、リセットより前の日時の記録は累計に入らない)。検査機の校正CSVは1行=1回の校正で、単票と同じく検査機の最終校正日・次回期限(省略時は校正周期から)を更新する。⑪**在庫オペレーションは1種別にまとめ、`Operation` 列で操作を選ぶ**(移動・数量調整・状態変更・分割・統合・振替・廃棄・返品・払出戻し。操作ごとに種別を分けると取込画面のパネルが9枚増えるため)。1行=1操作で、単票と同じ `LotOperationService` の同名メソッドを呼ぶ。操作ごとの必須列はコードで検査する(数量調整の理由は単票でも必須)。分割・振替は新ロット番号(`NewLotNumber`)を指定できるので、同じファイルの後の行から新しいロットを指せる。ロット系譜・状態履歴・監査ログは単票と同じに残る。⑫**物流の指示**:搬送指示CSVは番号が無いので1行=1件で、`Execute` の列で移動の実行まで進める。ピッキング指示CSVはピッキング番号(必須の手入力。`PK〜` は拒否)が同じ行を1件の指示にまとめ、1行=1明細(品目と数量)。ロット・ロケーションは単票と同じく先入れ先出し(有効期限優先。期限切れは引き当てない)で自動引当し、`Execute` で払出まで進める。棚卸指示CSVは棚卸番号(必須の手入力。`ST〜` は拒否)と対象ロケーションで作り、作成時点の在庫が明細になる。実棚数CSVは棚卸番号と明細のロット番号+ロケーションで指し、`Finalize` で確定(差異の棚卸調整)まで進める。⑬**不適合CSVは1行=1件の不適合**で、不適合番号(未登録なら新規起票。`NC〜` は拒否)か、番号を空欄にしてロット番号で指し、`Action`(対応指示)→`ActionRecord`(対応記録)→`Approve`(承認)の列があればその順に進める。検査の不合格で自動起票された不適合は番号が取り込むまで分からないため、**ロット番号で指せるのはそのロットの未完了(クローズ前)の不適合が1件だけのとき**に限る(0件・2件以上は行エラー。どれを進めるかを推測で決めない)。起票と対応記録は誰でも、対応指示と承認は単票と同じく品質管理の担当者だけ(行ごとに取り込んだユーザーのロールで判定)。⑭**サンプル品保管CSV**は1行=1件で、未登録のサンプル番号(`SP〜` は拒否)なら採取(在庫から抜く)、`Close`(払出・廃棄)を書けば保管を終える。登録済みの番号の行は保管の終了だけを行う - **CSVのZIP一括取込・一括出力**:複数のCSVをまとめたZIPを1回で取り込む(マスタは `POST api/masters/csv/bundle`、実績は `POST api/actuals/csv/bundle`)。**ファイル名の昇順に取り込み、種別はファイル名の最初の「_」より後ろで決める**(`01_work-centers.csv` → `work-centers`。同じ種別を番号違いで複数入れてよい)。CSV以外のファイル(README.md等)は無視する。**全ファイルを1つのトランザクションで処理し、どれか1ファイルでもエラーがあれば全ファイルを取り消す**(後のファイルが前のファイルの登録結果を参照するのでファイルごとに保存はするが、途中まで反映された状態が残るとどこから取り込み直せばよいか分からなくなるため)。エラーが出たファイルより後は処理せず、未処理として名前を返す。**マスタと実績が混ざったZIPは受け付けない**(取込の権限も対象画面も異なるため、別のZIPにする)。取込には**ZIPに含まれる全種別の取込権限**が要り、1種別でも権限が無ければ何も取り込まない。種別が分からないファイルは取り込む前に拒否する。ZIP爆弾対策として、CSVの数・1ファイルと合計の展開後サイズに上限を設け、読みながら数えて超えたら打ち切る。一括出力(`GET api/masters/csv/bundle`)は全マスタを取り込む順の番号付きファイル名でZIPにし、そのまま一括取込に使える(参照を絞っている種別は権限が無ければ含めない。ユーザーはパスワードを出力しないため、新しいDBへ移すときはユーザー系のファイルに初期パスワードを書き足す)。実績の一括出力は持たない @@ -676,6 +677,7 @@ DBはバックエンド(MesApp.Api)のみが保持し、既定はSQLiteと - **JWTの構成と保管**:アクセストークン(短命、既定60分)+リフレッシュトークン(既定12時間、交代勤務のシフト長を考慮)で構成する。アクセストークンはWebクライアントのメモリ上にのみ保持し、localStorage等の永続ストレージには保存しない(XSS時の窃取面を最小化)。リフレッシュトークンはHttpOnly Cookieで保持し、サイレント更新する - **リフレッシュトークンの再利用検知**:リフレッシュはローテーション(旧トークンを即失効し新トークンを発行)する。**失効済みトークンが再提示された場合は盗用の可能性があるものとして、そのユーザーの全リフレッシュトークンを失効させ**、監査ログに記録して再ログインを求める。正規の利用者も巻き添えで再ログインになるが、盗んだ側にセッションを継続させないことを優先する - **リフレッシュトークンの保持期間**:失効済み・期限切れのレコードは有効期間の2倍を過ぎた時点で削除する(発行のたびに当該ユーザー分を掃除する)。監査は監査ログ(7.6節)側で行うため、トークン行を残し続けない +- **アクセストークンの即時失効**:ユーザーの無効化・ロール変更・パスワード変更(本人の変更・管理者のリセット)は、発行済みのアクセストークンにも有効期限を待たずに効かせる。アクセストークンに Identity のセキュリティスタンプを載せ、API側(`Api/AccessTokenValidation`)がリクエストごとにDBのスタンプ・有効フラグと照合し、違えば401にする。ロール変更だけならリフレッシュトークンは失効させないので、クライアントはサイレント更新で新しいロールのトークンを取り直す。照合結果はキャッシュしない(キャッシュが切れるまで無効化が効かない時間ができるため。主キー検索1回のコストを許容する) - **JWT署名鍵**:発行側と検証側は同一のインスタンスから鍵を取る。鍵ファイルの自動生成は一時ファイルへ書いてから移動し、同時起動で別々の鍵を作らないようにする。UNIX系では鍵ファイルを所有者のみ読み書き可とする - **CSRF対策**:状態変更APIはすべてAuthorizationヘッダのJWTを必須とし(Cookieのみでは実行不可)、Cookie(リフレッシュトークン)には`HttpOnly`・`Secure`・`SameSite=Strict`を設定する。トークンリフレッシュ等Cookieのみで動作するエンドポイントへのクロスサイトからの発火はSameSite属性で遮断する - 通信は全てHTTPS。バックエンドはローカル(社内)環境で稼働するため公的CA証明書は取得できない前提とし、プライベートCA(社内CA or mkcert等)で発行した証明書をサーバーに設定し、CA証明書をクライアント端末(ブラウザ利用端末)に配布・信頼させる運用手順をドキュメント化する diff --git a/src/MesApp.Api/AccessTokenValidation.cs b/src/MesApp.Api/AccessTokenValidation.cs new file mode 100644 index 0000000..ea60f70 --- /dev/null +++ b/src/MesApp.Api/AccessTokenValidation.cs @@ -0,0 +1,43 @@ +using System.Security.Claims; +using MesApp.Core.Constants; +using MesApp.Infrastructure; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; + +namespace MesApp.Api; + +/// +/// アクセストークンの即時失効(Spec.md 7.4)。署名・有効期限の検証が通ったあとに、 +/// ユーザーが有効か、トークンのセキュリティスタンプがDBの値と一致するかを照合する。 +/// +/// +/// 署名と有効期限だけでは、無効化・ロール変更・パスワードリセットをしても発行済みのトークンが +/// 有効期限(既定60分)まで使えてしまう。リクエストごとに主キー検索が1回増えるが、 +/// キャッシュは挟まない(挟むとキャッシュが切れるまで無効化が効かない)。 +/// +public static class AccessTokenValidation +{ + /// JwtBearer の OnTokenValidated に渡す + public static async Task ValidateAsync(TokenValidatedContext context) + { + var principal = context.Principal; + var userId = principal?.FindFirstValue(ClaimTypes.NameIdentifier); + var stamp = principal?.FindFirstValue(MesClaimTypes.SecurityStamp); + if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(stamp)) + { + // スタンプを載せる前に発行されたトークンも弾く(クライアントはリフレッシュで取り直す) + context.Fail("トークンにユーザーまたはセキュリティスタンプがありません。"); + return; + } + + var db = context.HttpContext.RequestServices.GetRequiredService(); + var user = await db.Users.AsNoTracking() + .Where(u => u.Id == userId) + .Select(u => new { u.IsActive, u.SecurityStamp }) + .FirstOrDefaultAsync(context.HttpContext.RequestAborted); + if (user is null || !user.IsActive || user.SecurityStamp != stamp) + { + context.Fail("ユーザーが無効化されたか、権限・パスワードが変更されたためトークンは失効しています。"); + } + } +} diff --git a/src/MesApp.Api/Controllers/UsersController.cs b/src/MesApp.Api/Controllers/UsersController.cs index 7e8712c..8b1dde5 100644 --- a/src/MesApp.Api/Controllers/UsersController.cs +++ b/src/MesApp.Api/Controllers/UsersController.cs @@ -159,6 +159,12 @@ public async Task> Update(string id, UpdateUse await userManager.RemoveFromRolesAsync(user, currentRoles.Except(request.Roles)); await userManager.AddToRolesAsync(user, request.Roles.Except(currentRoles)); + // 無効化・ロール変更は発行済みのアクセストークンにも即時に効かせる(Spec.md 7.4)。 + // ロール変更だけならリフレッシュトークンは生きているので、クライアントは新しいロールで取り直す + if (deactivated || !currentRoles.ToHashSet().SetEquals(request.Roles)) + { + await userManager.UpdateSecurityStampAsync(user); + } if (deactivated) { // 無効化されたユーザーのセッションを止める(Spec.md 7.4) diff --git a/src/MesApp.Api/MesAppHost.cs b/src/MesApp.Api/MesAppHost.cs index f751548..22586aa 100644 --- a/src/MesApp.Api/MesAppHost.cs +++ b/src/MesApp.Api/MesAppHost.cs @@ -71,7 +71,10 @@ public static WebApplication Build(string[] args) }) .AddRoles() .AddEntityFrameworkStores() - .AddSignInManager(); + .AddSignInManager() + // 管理者によるパスワードリセット(ユーザーCSVの InitialPassword・初期管理者の再生成を含む)は + // リセット用トークンを発行してから使う。プロバイダが無いと発行で例外になる + .AddDefaultTokenProviders(); // JWT認証(アクセストークン。リフレッシュはRefreshTokenService+HttpOnly Cookie) builder.Services.Configure(builder.Configuration.GetSection(JwtOptions.SectionName)); @@ -128,6 +131,8 @@ public static WebApplication Build(string[] args) ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(1), }; + // 無効化・ロール変更・パスワード変更を発行済みトークンにも即時に効かせる(Spec.md 7.4) + options.Events = new JwtBearerEvents { OnTokenValidated = AccessTokenValidation.ValidateAsync }; }); builder.Services.AddAuthorization(); diff --git a/src/MesApp.Api/MustChangePasswordFilter.cs b/src/MesApp.Api/MustChangePasswordFilter.cs index b7f5437..a0a9492 100644 --- a/src/MesApp.Api/MustChangePasswordFilter.cs +++ b/src/MesApp.Api/MustChangePasswordFilter.cs @@ -15,8 +15,8 @@ namespace MesApp.Api; /// リクエストごとのDB参照はしない。 /// /// -/// 管理者によるパスワードリセット直後など、既に発行済みのトークンにはこのクレームが付かない。 -/// 反映は次回のトークン発行時(ロール変更と同じ扱い)。 +/// 管理者によるパスワードリセットでは、セキュリティスタンプが変わって発行済みのトークンが失効する +/// ()。再ログインで発行し直したトークンにこのクレームが付く。 /// /// public sealed class MustChangePasswordFilter : IAuthorizationFilter diff --git a/src/MesApp.Api/Services/CsvFile.cs b/src/MesApp.Api/Services/CsvFile.cs index 07128ae..1cd9a49 100644 --- a/src/MesApp.Api/Services/CsvFile.cs +++ b/src/MesApp.Api/Services/CsvFile.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Text; namespace MesApp.Api.Services; @@ -85,7 +86,7 @@ public static List Parse(string text) void EndRecord() { - fields.Add(field.ToString()); + fields.Add(Unprotect(field.ToString())); field.Clear(); if (fields.Count > 1 || fields[0].Trim().Length > 0) { @@ -128,7 +129,7 @@ void EndRecord() inQuotes = true; break; case ',': - fields.Add(field.ToString()); + fields.Add(Unprotect(field.ToString())); field.Clear(); break; case '\r': @@ -172,9 +173,34 @@ private static string Escape(string? value) { return string.Empty; } + if (IsFormulaLike(value)) + { + value = FormulaGuard + value; + } var needsQuotes = value.AsSpan().IndexOfAny(',', '"', '\n') >= 0 || value.Contains('\r') || value[0] == ' ' || value[^1] == ' '; return needsQuotes ? $"\"{value.Replace("\"", "\"\"")}\"" : value; } + + /// + /// 数式とみなされる値の前に付ける印。Excel等の表計算ソフトで開いたとき、利用者が入力した品目名や備考が + /// 数式(=HYPERLINK(...) やDDE)として実行されないようにする(CSVインジェクション対策。Spec.md 3.8) + /// + private const char FormulaGuard = '\''; + + /// + /// 表計算ソフトが数式として解釈する先頭文字か。+・- は数値(-5・+1.5)なら数式にならないので除く + /// (数量・座標の負値に印が付くと、表計算での集計や取り込み直しで数値として扱えなくなる) + /// + private static bool IsFormulaLike(string value) => value[0] switch + { + '=' or '@' or '\t' or '\r' => true, + '+' or '-' => !decimal.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out _), + _ => false, + }; + + /// 出力時に付けた印を外す(出力したCSVをそのまま取り込み直しても元の値に戻るようにする) + private static string Unprotect(string value) => + value.Length >= 2 && value[0] == FormulaGuard && IsFormulaLike(value[1..]) ? value[1..] : value; } diff --git a/src/MesApp.Api/Services/JwtTokenService.cs b/src/MesApp.Api/Services/JwtTokenService.cs index 06e7a96..cd04fec 100644 --- a/src/MesApp.Api/Services/JwtTokenService.cs +++ b/src/MesApp.Api/Services/JwtTokenService.cs @@ -22,6 +22,8 @@ public class JwtTokenService(SigningKeyProvider keyProvider, IOptions public const string MustChangePassword = "must_change_password"; + + /// + /// 発行時点のセキュリティスタンプ(Identity の SecurityStamp)。無効化・ロール変更・パスワード変更で + /// DB側の値が変わると、発行済みのアクセストークンは有効期限を待たずに使えなくなる + /// + public const string SecurityStamp = "security_stamp"; } diff --git a/tests/MesApp.Api.Tests/AuthTests.cs b/tests/MesApp.Api.Tests/AuthTests.cs index e833dc8..bbdd200 100644 --- a/tests/MesApp.Api.Tests/AuthTests.cs +++ b/tests/MesApp.Api.Tests/AuthTests.cs @@ -351,6 +351,62 @@ public async Task 信頼するプロキシが未設定なら転送ヘッダを /// プロキシ(remoteAddress)から、HTTPSで受けた ClientAddress のリクエストを転送された形でログインする。 /// HttpClient経由では接続元IPを指定できないため、TestServerに直接HttpContextを組み立てて渡す /// + [Fact] + public async Task ロール変更で発行済みのアクセストークンは失効しリフレッシュで新しいロールになる() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + using var worker = await TestAuth.CreateUserClientAsync(factory, admin, "worker1", "Passw0rd123", MesRoles.Operator); + Assert.Equal(HttpStatusCode.OK, (await worker.GetAsync("/api/locations")).StatusCode); + + var id = await UserIdAsync(admin, "worker1"); + (await admin.PutAsJsonAsync($"/api/users/{id}", + new UpdateUserRequest("worker1", [MesRoles.ProductionManager], IsActive: true))).EnsureSuccessStatusCode(); + + // 古いロールのトークンは有効期限を待たずに使えなくなる + Assert.Equal(HttpStatusCode.Unauthorized, (await worker.GetAsync("/api/locations")).StatusCode); + + // リフレッシュトークンは生きているので、取り直せば新しいロールで操作を続けられる + var refresh = await worker.PostAsync("/api/auth/refresh", null); + Assert.Equal(HttpStatusCode.OK, refresh.StatusCode); + var token = (await refresh.Content.ReadFromJsonAsync())!; + Assert.Equal([MesRoles.ProductionManager], token.User.Roles); + worker.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken); + Assert.Equal(HttpStatusCode.OK, (await worker.GetAsync("/api/locations")).StatusCode); + + // 表示名だけの更新ではトークンを失効させない + (await admin.PutAsJsonAsync($"/api/users/{id}", + new UpdateUserRequest("作業者1", [MesRoles.ProductionManager], IsActive: true))).EnsureSuccessStatusCode(); + Assert.Equal(HttpStatusCode.OK, (await worker.GetAsync("/api/locations")).StatusCode); + } + + [Fact] + public async Task 無効化とパスワードリセットで発行済みのアクセストークンは即時に失効する() + { + using var factory = new ApiFactory(); + using var admin = await TestAuth.CreateAdminClientAsync(factory); + using var retired = await TestAuth.CreateUserClientAsync(factory, admin, "retired1", "Passw0rd123", MesRoles.Operator); + using var reset = await TestAuth.CreateUserClientAsync(factory, admin, "reset1", "Passw0rd123", MesRoles.Operator); + + // 無効化:アクセストークンもリフレッシュも使えない + (await admin.PutAsJsonAsync($"/api/users/{await UserIdAsync(admin, "retired1")}", + new UpdateUserRequest("retired1", [MesRoles.Operator], IsActive: false))).EnsureSuccessStatusCode(); + Assert.Equal(HttpStatusCode.Unauthorized, (await retired.GetAsync("/api/locations")).StatusCode); + Assert.Equal(HttpStatusCode.Unauthorized, (await retired.PostAsync("/api/auth/refresh", null)).StatusCode); + + // 管理者によるパスワードリセット + (await admin.PostAsJsonAsync($"/api/users/{await UserIdAsync(admin, "reset1")}/reset-password", + new ResetPasswordRequest("NewPassw0rd456"))).EnsureSuccessStatusCode(); + Assert.Equal(HttpStatusCode.Unauthorized, (await reset.GetAsync("/api/locations")).StatusCode); + Assert.Equal(HttpStatusCode.Unauthorized, (await reset.PostAsync("/api/auth/refresh", null)).StatusCode); + + // 他のユーザー(管理者)のトークンには影響しない + Assert.Equal(HttpStatusCode.OK, (await admin.GetAsync("/api/locations")).StatusCode); + } + + private static async Task UserIdAsync(HttpClient admin, string userName) => + (await admin.GetFromJsonAsync>("/api/users"))!.Single(u => u.UserName == userName).Id; + private static Task LoginViaProxyAsync(ApiFactory factory, string remoteAddress) => factory.Server.SendAsync(context => { diff --git a/tests/MesApp.Api.Tests/MasterCsvTests.cs b/tests/MesApp.Api.Tests/MasterCsvTests.cs index 23b6b64..0f415da 100644 --- a/tests/MesApp.Api.Tests/MasterCsvTests.cs +++ b/tests/MesApp.Api.Tests/MasterCsvTests.cs @@ -626,6 +626,39 @@ public async Task 不良理由マスタをCSVで入出力できる() Assert.Contains("DF-01,寸法外れ(外径),Equipment,true", exported); } + [Fact] + public async Task CSV出力は数式とみなされる値に印を付け_取り込み直すと元の値に戻る() + { + using var factory = new ApiFactory(); + using var client = await TestAuth.CreateAdminClientAsync(factory); + var result = await ImportAsync(client, "defect-reasons", """ + Code,Name,Category,IsActive + DF-01,"=HYPERLINK(""http://example.invalid"",""x"")",Process,true + DF-02,@SUM(A1),Process,true + DF-03,-A1+1,Process,true + DF-04,-5,Process,true + DF-05,寸法外れ,Process,true + """); + Assert.Empty(result.Errors); + + // Excel等で開いたときに数式として実行されないよう ' を前に付ける。負の数・通常の文字列には付けない + var exported = Encoding.UTF8.GetString( + await (await client.GetAsync("/api/masters/csv/defect-reasons")).Content.ReadAsByteArrayAsync()); + Assert.Contains("DF-01,\"'=HYPERLINK(\"\"http://example.invalid\"\",\"\"x\"\")\",Process", exported); + Assert.Contains("DF-02,'@SUM(A1),Process", exported); + Assert.Contains("DF-03,'-A1+1,Process", exported); + Assert.Contains("DF-04,-5,Process", exported); + Assert.Contains("DF-05,寸法外れ,Process", exported); + + // 出力したCSVをそのまま取り込み直しても、印は外れて元の値のまま + var again = await ImportAsync(client, "defect-reasons", exported.TrimStart('')); + Assert.Empty(again.Errors); + var reasons = (await client.GetFromJsonAsync>("/api/defect-reasons"))!; + Assert.Equal("=HYPERLINK(\"http://example.invalid\",\"x\")", reasons.Single(r => r.Code == "DF-01").Name); + Assert.Equal("@SUM(A1)", reasons.Single(r => r.Code == "DF-02").Name); + Assert.Equal("-A1+1", reasons.Single(r => r.Code == "DF-03").Name); + } + [Fact] public async Task ユーザーCSVで最後のシステム管理者を降格できない() { From d2d6d1ec17a7889b94e016e429890e95498f168c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 14:58:40 +0000 Subject: [PATCH 05/11] =?UTF-8?q?=E4=BE=9D=E5=AD=98=E6=9B=B4=E6=96=B0=20Bu?= =?UTF-8?q?mp=20the=20actions=20group=20with=208=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the actions group with 8 updates: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `4` | `7` | | [actions/setup-dotnet](https://github.com/actions/setup-dotnet) | `4` | `6` | | [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4` | `7` | | [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) | `3` | `4` | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `3` | `4` | | [docker/login-action](https://github.com/docker/login-action) | `3` | `4` | | [docker/metadata-action](https://github.com/docker/metadata-action) | `5` | `6` | | [docker/build-push-action](https://github.com/docker/build-push-action) | `6` | `7` | Updates `actions/checkout` from 4 to 7 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v7) Updates `actions/setup-dotnet` from 4 to 6 - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v4...v6) Updates `actions/upload-artifact` from 4 to 7 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v7) Updates `docker/setup-qemu-action` from 3 to 4 - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4) Updates `docker/setup-buildx-action` from 3 to 4 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4) Updates `docker/login-action` from 3 to 4 - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v3...v4) Updates `docker/metadata-action` from 5 to 6 - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/v5...v6) Updates `docker/build-push-action` from 6 to 7 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/setup-dotnet dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: docker/setup-qemu-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: docker/setup-buildx-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: docker/login-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: docker/metadata-action dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: docker/build-push-action dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 6 +++--- .github/workflows/release.yml | 16 ++++++++-------- .github/workflows/vulnerability.yml | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7642810..f534916 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,9 +21,9 @@ jobs: runs-on: windows-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-dotnet@v4 + - uses: actions/setup-dotnet@v6 with: dotnet-version: 10.0.x @@ -35,7 +35,7 @@ jobs: - name: テスト結果を保存 if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: test-results path: TestResults diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aaec005..d9782c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,9 +24,9 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-dotnet@v4 + - uses: actions/setup-dotnet@v6 with: dotnet-version: 10.0.x @@ -51,27 +51,27 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 # コンパイルはビルド段でクロス発行するが、実行段の RUN(mkdir/chown)は対象アーキテクチャで動くため QEMU が要る - - uses: docker/setup-qemu-action@v3 - - uses: docker/setup-buildx-action@v3 + - uses: docker/setup-qemu-action@v4 + - uses: docker/setup-buildx-action@v4 - - uses: docker/login-action@v3 + - uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ github.token }} - id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ghcr.io/${{ github.repository_owner }}/parallelfactorymes tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - - uses: docker/build-push-action@v6 + - uses: docker/build-push-action@v7 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/vulnerability.yml b/.github/workflows/vulnerability.yml index eeec040..b3666fe 100644 --- a/.github/workflows/vulnerability.yml +++ b/.github/workflows/vulnerability.yml @@ -25,9 +25,9 @@ jobs: runs-on: windows-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-dotnet@v4 + - uses: actions/setup-dotnet@v6 with: dotnet-version: 10.0.x From 15e444e707b61e42672abb6d1d5941d9ab06a2af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:02:52 +0000 Subject: [PATCH 06/11] =?UTF-8?q?=E4=BE=9D=E5=AD=98=E6=9B=B4=E6=96=B0=20Bu?= =?UTF-8?q?mp=20the=20dotnet=20group=20with=2016=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps Microsoft.AspNetCore.Authentication.JwtBearer from 10.0.13 to 10.0.14 Bumps Microsoft.AspNetCore.Components.Authorization from 10.0.13 to 10.0.14 Bumps Microsoft.AspNetCore.Components.WebAssembly from 10.0.13 to 10.0.14 Bumps Microsoft.AspNetCore.Components.WebAssembly.DevServer from 10.0.13 to 10.0.14 Bumps Microsoft.AspNetCore.Components.WebAssembly.Server from 10.0.13 to 10.0.14 Bumps Microsoft.AspNetCore.Identity.EntityFrameworkCore from 10.0.13 to 10.0.14 Bumps Microsoft.AspNetCore.Mvc.Testing from 10.0.13 to 10.0.14 Bumps Microsoft.AspNetCore.OpenApi from 10.0.13 to 10.0.14 Bumps Microsoft.EntityFrameworkCore.Design from 10.0.13 to 10.0.14 Bumps Microsoft.EntityFrameworkCore.Sqlite from 10.0.13 to 10.0.14 Bumps Microsoft.EntityFrameworkCore.SqlServer from 10.0.13 to 10.0.14 Bumps Microsoft.Extensions.Identity.Stores from 10.0.13 to 10.0.14 Bumps Microsoft.Extensions.Localization from 10.0.13 to 10.0.14 Bumps Microsoft.OpenApi from 2.12.2 to 2.13.0 Bumps Microsoft.Web.WebView2 from 1.0.4129.50 to 1.0.4314.35 Bumps System.Security.Cryptography.Pkcs from 10.0.13 to 10.0.14 --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.Authentication.JwtBearer dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.AspNetCore.Components.Authorization dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.AspNetCore.Components.WebAssembly dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.AspNetCore.Components.WebAssembly.DevServer dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.AspNetCore.Components.WebAssembly.Server dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.AspNetCore.Identity.EntityFrameworkCore dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.AspNetCore.Mvc.Testing dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.AspNetCore.OpenApi dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.EntityFrameworkCore.Design dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.EntityFrameworkCore.Sqlite dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.EntityFrameworkCore.SqlServer dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.Extensions.Identity.Stores dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.Extensions.Localization dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: Microsoft.OpenApi dependency-version: 2.13.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dotnet - dependency-name: Microsoft.Web.WebView2 dependency-version: 1.0.4314.35 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet - dependency-name: System.Security.Cryptography.Pkcs dependency-version: 10.0.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet ... Signed-off-by: dependabot[bot] --- src/MesApp.Api/MesApp.Api.csproj | 10 +++++----- src/MesApp.Client.Web/MesApp.Client.Web.csproj | 8 ++++---- src/MesApp.Core/MesApp.Core.csproj | 2 +- src/MesApp.Desktop/MesApp.Desktop.csproj | 2 +- src/MesApp.Infrastructure/MesApp.Infrastructure.csproj | 8 ++++---- tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/MesApp.Api/MesApp.Api.csproj b/src/MesApp.Api/MesApp.Api.csproj index 228060b..7a794c6 100644 --- a/src/MesApp.Api/MesApp.Api.csproj +++ b/src/MesApp.Api/MesApp.Api.csproj @@ -12,14 +12,14 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/MesApp.Client.Web/MesApp.Client.Web.csproj b/src/MesApp.Client.Web/MesApp.Client.Web.csproj index 0f9138e..c19f00d 100644 --- a/src/MesApp.Client.Web/MesApp.Client.Web.csproj +++ b/src/MesApp.Client.Web/MesApp.Client.Web.csproj @@ -9,10 +9,10 @@ - - - - + + + + diff --git a/src/MesApp.Core/MesApp.Core.csproj b/src/MesApp.Core/MesApp.Core.csproj index ee850e5..30e6a2b 100644 --- a/src/MesApp.Core/MesApp.Core.csproj +++ b/src/MesApp.Core/MesApp.Core.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/MesApp.Desktop/MesApp.Desktop.csproj b/src/MesApp.Desktop/MesApp.Desktop.csproj index 778c147..0c72645 100644 --- a/src/MesApp.Desktop/MesApp.Desktop.csproj +++ b/src/MesApp.Desktop/MesApp.Desktop.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj b/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj index 250f842..5debf6c 100644 --- a/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj +++ b/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj @@ -5,13 +5,13 @@ - - - + + + - + diff --git a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj index bb1decf..1640726 100644 --- a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj +++ b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj @@ -9,7 +9,7 @@ - + From c3c157cd051ea51bc752c2d01af131697ede5f0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:04:00 +0000 Subject: [PATCH 07/11] =?UTF-8?q?=E4=BE=9D=E5=AD=98=E6=9B=B4=E6=96=B0=20Bu?= =?UTF-8?q?mp=20the=20others=20group=20with=203=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps bunit from 2.9.0 to 2.13.3 Bumps SQLitePCLRaw.bundle_e_sqlite3 from 3.0.4 to 3.0.5 Bumps xunit.runner.visualstudio from 3.1.4 to 3.1.5 --- updated-dependencies: - dependency-name: bunit dependency-version: 2.13.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: others - dependency-name: SQLitePCLRaw.bundle_e_sqlite3 dependency-version: 3.0.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: others - dependency-name: xunit.runner.visualstudio dependency-version: 3.1.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: others - dependency-name: xunit.runner.visualstudio dependency-version: 3.1.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: others ... Signed-off-by: dependabot[bot] --- src/MesApp.Infrastructure/MesApp.Infrastructure.csproj | 2 +- tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj | 2 +- tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj b/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj index 250f842..5520b45 100644 --- a/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj +++ b/src/MesApp.Infrastructure/MesApp.Infrastructure.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj index bb1decf..5d4596e 100644 --- a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj +++ b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj b/tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj index 9f37ced..b3d9d32 100644 --- a/tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj +++ b/tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj @@ -6,10 +6,10 @@ - + - + From 98d8c4b64d266836490ec926706722aa437c5c1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:05:39 +0000 Subject: [PATCH 08/11] =?UTF-8?q?=E4=BE=9D=E5=AD=98=E6=9B=B4=E6=96=B0=20Bu?= =?UTF-8?q?mp=20Microsoft.NET.Test.Sdk=20from=2017.14.1=20to=2018.11.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-version: 18.11.0 dependency-type: direct:production update-type: version-update:semver-major - dependency-name: Microsoft.NET.Test.Sdk dependency-version: 18.11.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj | 2 +- tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj index bb1decf..1234cc9 100644 --- a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj +++ b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj b/tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj index 9f37ced..10ceb05 100644 --- a/tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj +++ b/tests/MesApp.Client.Web.Tests/MesApp.Client.Web.Tests.csproj @@ -7,7 +7,7 @@ - + From 00df53498b9ec9d801f9b4f599557fe228d3cd67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:31:32 +0000 Subject: [PATCH 09/11] =?UTF-8?q?=E4=BE=9D=E5=AD=98=E6=9B=B4=E6=96=B0=20Bu?= =?UTF-8?q?mp=20coverlet.collector=20from=206.0.4=20to=2010.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- updated-dependencies: - dependency-name: coverlet.collector dependency-version: 10.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj index b1a84e5..20c1512 100644 --- a/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj +++ b/tests/MesApp.Api.Tests/MesApp.Api.Tests.csproj @@ -8,7 +8,7 @@ - + From 610c3b7f416d80698e54abbb616e64b0c3634527 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Tue, 3 Nov 2026 01:14:10 +0900 Subject: [PATCH 10/11] =?UTF-8?q?dotnet-ef=20=E3=82=92=20EF=20Core=20?= =?UTF-8?q?=E3=81=AE=E3=83=91=E3=83=83=E3=82=B1=E3=83=BC=E3=82=B8=E3=81=A8?= =?UTF-8?q?=E5=90=8C=E3=81=98=2010.0.14=20=E3=81=AB=E6=8F=83=E3=81=88?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet-tools.json b/dotnet-tools.json index 39007e0..5e9fe39 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "10.0.13", + "version": "10.0.14", "commands": [ "dotnet-ef" ], From bc99ba535bb7acb54e0211963905d3e13e1153e9 Mon Sep 17 00:00:00 2001 From: msmsrep <113223770+msmsrep@users.noreply.github.com> Date: Tue, 3 Nov 2026 10:39:27 +0900 Subject: [PATCH 11/11] =?UTF-8?q?Microsoft.OpenApi=20=E3=81=AE=E3=83=A1?= =?UTF-8?q?=E3=82=B8=E3=83=A3=E3=83=BC=E6=9B=B4=E6=96=B0=E3=82=92=20Depend?= =?UTF-8?q?abot=20=E3=81=AE=E5=AF=BE=E8=B1=A1=E3=81=8B=E3=82=89=E5=A4=96?= =?UTF-8?q?=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0031fbc..58fc95c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,6 +14,11 @@ updates: - msmsrep commit-message: prefix: "依存更新" + ignore: + # Microsoft.AspNetCore.OpenApi 10.x は Microsoft.OpenApi 3.0 未満に制限している。.NET 11 へ移行するときに外す + - dependency-name: Microsoft.OpenApi + update-types: + - version-update:semver-major groups: # Microsoft.* / System.* は同じサービシングで揃えて上げる(版がずれると NU1605 の格下げエラーになる) dotnet: