26ff8d9b4f
Set up repository with legacy .NET Framework 4.8 source (OLD/), new .NET 10 Blazor solution (NEW/), OpenSpec specifications, documentation, and project configuration.
195 lines
8.2 KiB
Markdown
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 |
|