Files
Joseph Doherty 26ff8d9b4f 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.
2026-01-02 07:43:29 -05:00

195 lines
8.2 KiB
Markdown

# 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 |