Initial commit: JDE Scoping Tool migration project
Set up repository with legacy .NET Framework 4.8 source (OLD/), new .NET 10 Blazor solution (NEW/), OpenSpec specifications, documentation, and project configuration.
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
# Web API and Authentication - Migration Deltas
|
||||
|
||||
## Purpose
|
||||
|
||||
This document specifies requirements ADDED or MODIFIED for the .NET 10 migration of the Web API and authentication layer. These requirements extend the base specification at `openspec/specs/web-api-auth/spec.md`.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Cross-Platform LDAP Support
|
||||
|
||||
The system SHALL use `System.DirectoryServices.Protocols` for LDAP authentication to ensure cross-platform compatibility.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use `System.DirectoryServices.DirectoryEntry` or `System.DirectoryServices.DirectorySearcher` (Windows-only APIs)
|
||||
- The system SHALL use `LdapConnection` from `System.DirectoryServices.Protocols`
|
||||
- The system SHALL use asynchronous patterns with `Task.Run()` for LDAP operations (the LDAP API is synchronous)
|
||||
- The system SHALL dispose `LdapConnection` after each operation
|
||||
|
||||
#### Scenario: LDAP authentication on Linux
|
||||
|
||||
- **WHEN** the application runs on Linux with LDAP configuration
|
||||
- **THEN** the system authenticates successfully using `System.DirectoryServices.Protocols`
|
||||
|
||||
### Requirement: ClosedXML for Excel Operations
|
||||
|
||||
The system SHALL use ClosedXML library for Excel file parsing in the File API.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use EPPlus (commercial license required in v7+)
|
||||
- The system SHALL use `XLWorkbook` from ClosedXML for reading uploaded Excel files
|
||||
- The system SHALL use 1-indexed row/column access matching ClosedXML conventions
|
||||
- The system SHALL handle empty worksheets gracefully using `LastRowUsed()?.RowNumber()`
|
||||
|
||||
#### Scenario: Parse Excel upload with ClosedXML
|
||||
|
||||
- **WHEN** a user uploads an Excel file to any file upload endpoint
|
||||
- **THEN** the system parses the file using ClosedXML and returns matching data
|
||||
|
||||
### Requirement: OpenAPI Documentation
|
||||
|
||||
The system SHALL provide OpenAPI/Swagger documentation for all API endpoints.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL use Swashbuckle.AspNetCore for OpenAPI generation
|
||||
- The system SHALL expose Swagger UI at `/swagger` in development mode
|
||||
- The system SHALL document all endpoints with correct HTTP methods and response types
|
||||
- The system SHALL document authentication requirements for protected endpoints
|
||||
|
||||
#### Scenario: Developer accesses API documentation
|
||||
|
||||
- **WHEN** a developer navigates to `/swagger` in development mode
|
||||
- **THEN** the system displays interactive API documentation for all endpoints
|
||||
|
||||
### Requirement: JSON Serialization with System.Text.Json
|
||||
|
||||
The system SHALL use System.Text.Json for all JSON serialization.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use Newtonsoft.Json (legacy)
|
||||
- The system SHALL use `JsonStringEnumConverter` for enum serialization
|
||||
- The system SHALL configure JSON options via `AddControllers().AddJsonOptions()`
|
||||
- The system SHALL return JSON responses (not redirects) for all API errors
|
||||
|
||||
#### Scenario: Enum serialization in API response
|
||||
|
||||
- **WHEN** an API endpoint returns a model with an enum property
|
||||
- **THEN** the system serializes the enum as a string (e.g., "Submitted" not 1)
|
||||
|
||||
### Requirement: IHubContext Dependency Injection
|
||||
|
||||
The system SHALL use `IHubContext<StatusHub>` for publishing SignalR updates from outside the hub.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use static `GlobalHost.ConnectionManager` pattern (legacy)
|
||||
- The system SHALL inject `IHubContext<StatusHub>` into controllers and services
|
||||
- The system SHALL use `Clients.All.SendAsync()` for broadcasting
|
||||
- The system SHALL handle SignalR publish failures gracefully (log and continue)
|
||||
|
||||
#### Scenario: Controller publishes search update
|
||||
|
||||
- **WHEN** SearchController creates a new search
|
||||
- **THEN** the system uses injected `IHubContext<StatusHub>` to broadcast the update
|
||||
|
||||
### Requirement: Async-First Pattern
|
||||
|
||||
The system SHALL use async/await patterns for all I/O operations.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- All controller actions SHALL be async with `CancellationToken` parameter
|
||||
- All service methods SHALL be async with `CancellationToken` parameter
|
||||
- The system SHALL use `IAsyncEnumerable` for streaming scenarios where applicable
|
||||
- The system SHALL propagate `CancellationToken` to all downstream async calls
|
||||
|
||||
#### Scenario: Cancellation during long-running operation
|
||||
|
||||
- **WHEN** a client cancels a request during LDAP authentication
|
||||
- **THEN** the system throws `OperationCanceledException` and stops the operation
|
||||
|
||||
### Requirement: Structured Logging
|
||||
|
||||
The system SHALL use `ILogger<T>` for structured logging.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use NLog directly (legacy)
|
||||
- The system SHALL inject `ILogger<T>` into all controllers and services
|
||||
- The system SHALL use structured logging with named parameters: `_logger.LogWarning("Failed to authenticate {Username}", username)`
|
||||
- The system SHALL log at appropriate levels (Information, Warning, Error)
|
||||
|
||||
#### Scenario: Failed LDAP authentication logged
|
||||
|
||||
- **WHEN** LDAP authentication fails
|
||||
- **THEN** the system logs a warning with structured username and error details
|
||||
|
||||
### Requirement: Options Pattern Configuration
|
||||
|
||||
The system SHALL use `IOptions<T>` for strongly-typed configuration.
|
||||
|
||||
#### Business Rules
|
||||
|
||||
- The system SHALL NOT use `ConfigurationManager` or `WebConfigurationManager` (legacy)
|
||||
- The system SHALL bind `LdapOptions` from `Ldap` configuration section
|
||||
- The system SHALL bind `AuthOptions` from `Auth` configuration section
|
||||
- The system SHALL inject `IOptions<T>` or `IOptionsSnapshot<T>` as needed
|
||||
|
||||
#### Scenario: Configuration changes without restart
|
||||
|
||||
- **WHEN** configuration values change in appsettings.json
|
||||
- **THEN** the system can use `IOptionsSnapshot<T>` to read updated values
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Cookie Authentication Events (Modified)
|
||||
|
||||
The system SHALL suppress cookie authentication redirects and return HTTP status codes for Blazor WASM compatibility.
|
||||
|
||||
#### Modified Business Rules
|
||||
|
||||
- The system SHALL configure `OnRedirectToLogin` to return HTTP 401 instead of redirect
|
||||
- The system SHALL configure `OnRedirectToAccessDenied` to return HTTP 403 instead of redirect
|
||||
- The system SHALL return JSON error responses (not HTML) for authentication failures
|
||||
|
||||
#### Scenario: Unauthenticated API request from Blazor
|
||||
|
||||
- **WHEN** an unauthenticated Blazor WASM client requests a protected endpoint
|
||||
- **THEN** the system returns HTTP 401 with JSON error body (no redirect)
|
||||
|
||||
### Requirement: SignalR Hub Endpoint Path (Modified)
|
||||
|
||||
The system SHALL map the StatusHub to the `/hubs/status` endpoint path.
|
||||
|
||||
#### Modified Business Rules
|
||||
|
||||
- The system SHALL map StatusHub to `/hubs/status` endpoint
|
||||
- The system SHALL configure SignalR with default settings (no custom protocols)
|
||||
|
||||
#### Scenario: Client connects to SignalR hub
|
||||
|
||||
- **WHEN** a Blazor WASM client connects to SignalR
|
||||
- **THEN** the system accepts connections at `/hubs/status`
|
||||
|
||||
### Requirement: Dependency Injected Memory Cache (Modified)
|
||||
|
||||
The system SHALL use DI-injected `IMemoryCache` for file template caching.
|
||||
|
||||
#### Modified Business Rules
|
||||
|
||||
- The system SHALL inject `IMemoryCache` via constructor (not `MemoryCache.Default`)
|
||||
- The system SHALL use `MemoryCacheEntryOptions` with `AbsoluteExpirationRelativeToNow`
|
||||
- The system SHALL use `TryGetValue<T>` pattern for type-safe cache retrieval
|
||||
|
||||
#### Scenario: File template cached with DI cache
|
||||
|
||||
- **WHEN** a file template is generated
|
||||
- **THEN** the system stores it in the injected `IMemoryCache` with 1-minute expiration
|
||||
|
||||
## Migration Notes
|
||||
|
||||
| Legacy Pattern | New Pattern | Rationale |
|
||||
|----------------|-------------|-----------|
|
||||
| `System.DirectoryServices.DirectoryEntry` | `System.DirectoryServices.Protocols.LdapConnection` | Cross-platform compatibility |
|
||||
| EPPlus | ClosedXML | MIT license (EPPlus commercial in v7+) |
|
||||
| Newtonsoft.Json | System.Text.Json | Built-in, better performance |
|
||||
| `GlobalHost.ConnectionManager.GetHubContext<T>()` | `IHubContext<T>` via DI | Standard DI pattern, testable |
|
||||
| NLog | `ILogger<T>` | Built-in logging abstraction |
|
||||
| `WebConfigurationManager.AppSettings` | `IOptions<T>` | Strongly-typed configuration |
|
||||
| `MemoryCache.Default` | `IMemoryCache` via DI | Standard caching abstraction |
|
||||
| Login redirect on 401 | HTTP 401 response | Blazor WASM SPA compatibility |
|
||||
Reference in New Issue
Block a user